@adatechnology/game-characters-three 0.1.4 → 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/README.md +5 -0
- package/dist/index.d.ts +171 -21
- package/dist/index.global.js +557 -53
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +357 -53
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
package/dist/index.js
CHANGED
|
@@ -686,7 +686,7 @@ var createBestBuilder = (THREE, { types, random = Math.random }) => {
|
|
|
686
686
|
const TORSO_P = [[0.18, 0.86], [0.205, 0.95], [0.195, 1.03], [0.19, 1.12], [0.225, 1.27], [0.26, 1.4], [0.265, 1.47], [0.21, 1.55], [0.085, 1.6]];
|
|
687
687
|
const TORSO_F = [[0.17, 0.86], [0.215, 0.95], [0.2, 1.02], [0.165, 1.1], [0.2, 1.25], [0.245, 1.38], [0.25, 1.46], [0.2, 1.54], [0.085, 1.6]];
|
|
688
688
|
const torsoGeo = (sx, sz, fem) => {
|
|
689
|
-
const g = new THREE.LatheGeometry(new THREE.SplineCurve((fem ? TORSO_F : TORSO_P).map((
|
|
689
|
+
const g = new THREE.LatheGeometry(new THREE.SplineCurve((fem ? TORSO_F : TORSO_P).map((p2) => new THREE.Vector2(p2[0], p2[1]))).getPoints(Math.max(8, Math.round(30 * LK))), Math.max(6, Math.round(24 * LK)));
|
|
690
690
|
g.scale(1.12 * (sx || 1), 1, 0.64 * (sz || 1));
|
|
691
691
|
return g;
|
|
692
692
|
};
|
|
@@ -774,34 +774,34 @@ var createBestBuilder = (THREE, { types, random = Math.random }) => {
|
|
|
774
774
|
const skinFinal = P.char ? 1840658 : P.wet ? col(skinC, 8032160, 0.55) : skinC;
|
|
775
775
|
const skinM = M(skinFinal);
|
|
776
776
|
const topM = M(P.char ? col(topC, 1380880, 0.75) : P.wet ? col(topC, 1712688, 0.45) : col(topC, GRAY, d * 0.3)), pantsM = M(col(pantC, GRAY, d * 0.3)), shoeM = M(T.shoeColor), red = M(RED);
|
|
777
|
-
const add = (
|
|
777
|
+
const add = (p2, geo, m, x, y, z2, rx, ry, rz) => {
|
|
778
778
|
const o = new THREE.Mesh(geo, m);
|
|
779
779
|
o.position.set(x || 0, y || 0, z2 || 0);
|
|
780
780
|
if (rx || ry || rz) o.rotation.set(rx || 0, ry || 0, rz || 0);
|
|
781
|
-
|
|
781
|
+
p2.add(o);
|
|
782
782
|
return o;
|
|
783
783
|
};
|
|
784
|
-
const ball = (
|
|
784
|
+
const ball = (p2, r, m, x, y, z2) => add(p2, SPH(r, SEG, 12), m, x, y, z2);
|
|
785
785
|
topM_ref = topM;
|
|
786
786
|
const SKM = {}, skM = (c) => SKM[c] || (SKM[c] = new THREE.MeshStandardMaterial({ color: c, roughness: 0.3, metalness: 0, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 }));
|
|
787
|
-
const flat = (
|
|
787
|
+
const flat = (p2, x, y, zz, sx, sy, c) => {
|
|
788
788
|
const m = new THREE.Mesh(BLOB, skM(c));
|
|
789
789
|
m.rotation.set(PI / 2, 0, BR() * 6.28);
|
|
790
790
|
m.scale.set(sx, 1, sy);
|
|
791
791
|
m.position.set(x, y, zz);
|
|
792
|
-
|
|
792
|
+
p2.add(m);
|
|
793
793
|
return m;
|
|
794
794
|
};
|
|
795
|
-
const splat = (
|
|
796
|
-
const a = flat(
|
|
795
|
+
const splat = (p2, x, y, zz, r, c, n) => {
|
|
796
|
+
const a = flat(p2, x, y, zz + 4e-3, r, r * (0.7 + BR() * 0.5), c);
|
|
797
797
|
for (let k = 0; k < (n || 0); k++) {
|
|
798
798
|
const an = BR() * 6.28, dd = r * (1.2 + BR() * 0.9), q = r * (0.12 + BR() * 0.18);
|
|
799
|
-
flat(
|
|
799
|
+
flat(p2, x + Math.cos(an) * dd, y + Math.sin(an) * dd, zz + 4e-3, q, q * 0.7, c);
|
|
800
800
|
}
|
|
801
|
-
if (n) run(
|
|
801
|
+
if (n) run(p2, x + r * 0.15, y - r * 0.5, y - r * (2.2 + BR() * 1.2), zz + 3e-3, zz, c, r * 0.35);
|
|
802
802
|
return a;
|
|
803
803
|
};
|
|
804
|
-
const run = (
|
|
804
|
+
const run = (p2, x, y0, y1, z0, z1, c, w) => {
|
|
805
805
|
const L = y0 - y1, sh = new THREE.Shape();
|
|
806
806
|
sh.moveTo(-w / 2, 0);
|
|
807
807
|
sh.lineTo(w / 2, 0);
|
|
@@ -811,30 +811,30 @@ var createBestBuilder = (THREE, { types, random = Math.random }) => {
|
|
|
811
811
|
const g = new THREE.ShapeGeometry(sh), m = new THREE.Mesh(g, skM(c));
|
|
812
812
|
m.position.set(x, y0, z0);
|
|
813
813
|
m.rotation.x = -Math.atan2(z0 - z1, L);
|
|
814
|
-
|
|
814
|
+
p2.add(m);
|
|
815
815
|
const bl = new THREE.Mesh(SPH(w * 0.42, 10, 8), skM(c));
|
|
816
816
|
bl.scale.set(1, 1.35, 0.5);
|
|
817
817
|
bl.position.set(x + w * 0.03, y1, z1);
|
|
818
|
-
|
|
818
|
+
p2.add(bl);
|
|
819
819
|
return bl;
|
|
820
820
|
};
|
|
821
|
-
const stump = (
|
|
822
|
-
const st = ball(
|
|
821
|
+
const stump = (p2, r, y, up) => {
|
|
822
|
+
const st = ball(p2, r * 1.02, red, 0, y, 0);
|
|
823
823
|
st.scale.set(1, 0.35, 1);
|
|
824
|
-
add(
|
|
824
|
+
add(p2, CYL(r * 0.32, r * 0.36, 0.07, 10), M(15261900), 0, y + (up ? 0.04 : -0.04), 0);
|
|
825
825
|
};
|
|
826
|
-
const ring = (
|
|
827
|
-
const o = add(
|
|
826
|
+
const ring = (p2, r, h, m, y, sx, sz) => {
|
|
827
|
+
const o = add(p2, CYL(r, r, h, 24), m, 0, y, 0);
|
|
828
828
|
o.scale.set(sx || 1.12, 1, sz || 0.7);
|
|
829
829
|
return o;
|
|
830
830
|
};
|
|
831
831
|
const root = new THREE.Group(), fig = new THREE.Group();
|
|
832
|
-
const drip = (
|
|
832
|
+
const drip = (p2, x, y, zz, kind, every) => {
|
|
833
833
|
if (d >= 3 && kind !== "bile") return;
|
|
834
834
|
const o = new THREE.Object3D();
|
|
835
835
|
o.position.set(x, y, zz);
|
|
836
836
|
o.userData.drip = { kind: kind || (d < 1 ? "blood" : "old"), every: every || (d < 1 ? 1.2 : 2.4) };
|
|
837
|
-
|
|
837
|
+
p2.add(o);
|
|
838
838
|
};
|
|
839
839
|
fig.scale.set(P.s * P.bulk, P.s, P.s * Math.min(1.15, P.bulk));
|
|
840
840
|
fig.position.y = P.drop * P.s;
|
|
@@ -2007,7 +2007,7 @@ var createPersonKit = (THREE, { random: R, createCanvas = () => document.createE
|
|
|
2007
2007
|
}
|
|
2008
2008
|
function torsoGeo(fem) {
|
|
2009
2009
|
const P = fem ? [[0.17, 0.86], [0.215, 0.95], [0.2, 1.02], [0.165, 1.1], [0.2, 1.25], [0.245, 1.38], [0.25, 1.46], [0.2, 1.54], [0.085, 1.6]] : [[0.18, 0.86], [0.205, 0.95], [0.195, 1.03], [0.19, 1.12], [0.225, 1.27], [0.26, 1.4], [0.265, 1.47], [0.21, 1.55], [0.085, 1.6]];
|
|
2010
|
-
const pts = new THREE.SplineCurve(P.map((
|
|
2010
|
+
const pts = new THREE.SplineCurve(P.map((p2) => new THREE.Vector2(p2[0], p2[1]))).getPoints(30);
|
|
2011
2011
|
const g = new THREE.LatheGeometry(pts, 24);
|
|
2012
2012
|
g.scale(1.12, 1, 0.64);
|
|
2013
2013
|
return g;
|
|
@@ -2362,15 +2362,15 @@ var createPersonBuilder = (THREE, options = {}) => {
|
|
|
2362
2362
|
}
|
|
2363
2363
|
const MOVES_IDLE = MOVES.idle;
|
|
2364
2364
|
function pose(c, z, W, t) {
|
|
2365
|
-
const
|
|
2366
|
-
const breathe = reduce ? 0 : Math.sin(
|
|
2365
|
+
const p2 = c.ph + t, freq = z ? 3.4 : 5.6, ph = t * freq + c.ph;
|
|
2366
|
+
const breathe = reduce ? 0 : Math.sin(p2 * 1.6) * 0.012;
|
|
2367
2367
|
c.upper.scale.y = 1 + breathe;
|
|
2368
2368
|
const hunch = c.hunch + (z ? 0.38 : 0) + W * 0.05;
|
|
2369
2369
|
c.upper.rotation.x = hunch;
|
|
2370
2370
|
c.upper.rotation.y = W * Math.sin(ph) * 0.1;
|
|
2371
2371
|
c.upper.rotation.z = z ? Math.sin(ph * 0.5) * 0.06 * W + 0.05 : 0;
|
|
2372
2372
|
c.fig.position.y = -W * Math.abs(Math.sin(ph)) * 0.035;
|
|
2373
|
-
c.headG.rotation.y = reduce ? 0 : Math.sin(
|
|
2373
|
+
c.headG.rotation.y = reduce ? 0 : Math.sin(p2 * 0.5) * 0.25 * (z ? 0.3 : 1) * (W ? 0.4 : 1) * (t ? 1 : 0);
|
|
2374
2374
|
c.headG.rotation.x = -hunch * 0.6 + (z ? 0.15 : 0);
|
|
2375
2375
|
c.headG.rotation.z = z ? 0.22 : 0;
|
|
2376
2376
|
c.legs.forEach((l, k) => {
|
|
@@ -2383,7 +2383,7 @@ var createPersonBuilder = (THREE, options = {}) => {
|
|
|
2383
2383
|
const raise = z ? 1 : 0;
|
|
2384
2384
|
const base = a.hold ? -0.2 : 0;
|
|
2385
2385
|
const swing = -a.s * Math.sin(ph) * (z ? 0.12 : 0.42) * W * (a.grip ? 0 : 1);
|
|
2386
|
-
a.g.rotation.x = (a.grip && !z ? -0.75 : base - raise * (z ? 1.12 : 1.3)) + swing + (z && !reduce && t ? Math.sin(
|
|
2386
|
+
a.g.rotation.x = (a.grip && !z ? -0.75 : base - raise * (z ? 1.12 : 1.3)) + swing + (z && !reduce && t ? Math.sin(p2 * 3 + a.s * 2) * 0.08 : 0);
|
|
2387
2387
|
a.g.rotation.z = a.s * (0.07 + (z ? 0.1 : 0)) + (a.grip && !z ? -a.s * 0.35 : 0);
|
|
2388
2388
|
a.el.rotation.x = z ? -0.18 : a.grip ? -1.1 : -(0.12 + (a.hold ? 0.25 : 0) + W * (0.25 + Math.max(0, a.s * Math.sin(ph)) * 0.3));
|
|
2389
2389
|
if (a.fingers) {
|
|
@@ -2408,16 +2408,16 @@ var createPersonBuilder = (THREE, options = {}) => {
|
|
|
2408
2408
|
c.mouth.visible = true;
|
|
2409
2409
|
c.bags.material.opacity = 0.9;
|
|
2410
2410
|
}
|
|
2411
|
-
function buildPerson(
|
|
2411
|
+
function buildPerson(p2, zed) {
|
|
2412
2412
|
const root = new THREE.Group(), chars = [];
|
|
2413
|
-
if (
|
|
2414
|
-
const c = build(
|
|
2413
|
+
if (p2.b) {
|
|
2414
|
+
const c = build(p2.b, 0);
|
|
2415
2415
|
chars.push(c);
|
|
2416
2416
|
root.add(c.root);
|
|
2417
|
-
if (zed && !
|
|
2418
|
-
c.z = zed && !
|
|
2417
|
+
if (zed && !p2.kid) zombify(c, p2.zcat);
|
|
2418
|
+
c.z = zed && !p2.kid;
|
|
2419
2419
|
}
|
|
2420
|
-
(
|
|
2420
|
+
(p2.kids || []).forEach((k) => {
|
|
2421
2421
|
const c = build(k, 0);
|
|
2422
2422
|
c.root.position.x = k.x;
|
|
2423
2423
|
c.root.rotation.y = -k.x * 0.4;
|
|
@@ -2675,11 +2675,11 @@ var createFlyingPieces = (three, { random, groundAt = () => 0, onRest, max = 40
|
|
|
2675
2675
|
};
|
|
2676
2676
|
return {
|
|
2677
2677
|
pieces,
|
|
2678
|
-
launch(source,
|
|
2678
|
+
launch(source, scene2, direction, dress, tag) {
|
|
2679
2679
|
source.updateWorldMatrix(true, true);
|
|
2680
2680
|
const clone = source.clone(true);
|
|
2681
2681
|
source.matrixWorld.decompose(clone.position, clone.quaternion, clone.scale);
|
|
2682
|
-
|
|
2682
|
+
scene2.add(clone);
|
|
2683
2683
|
dress?.(clone);
|
|
2684
2684
|
return add(
|
|
2685
2685
|
clone,
|
|
@@ -2829,7 +2829,7 @@ var woundBodyOf = (root) => {
|
|
|
2829
2829
|
import { segmentsBelow } from "@adatechnology/game-characters";
|
|
2830
2830
|
var worldPosition = (three, object) => object.getWorldPosition(new three.Vector3());
|
|
2831
2831
|
var createWounds = (three, options) => {
|
|
2832
|
-
const { random, scene, bridge } = options;
|
|
2832
|
+
const { random, scene: scene2, bridge, sound } = options;
|
|
2833
2833
|
const materials = options.materials ?? createWoundMaterials(three);
|
|
2834
2834
|
const flying = createFlyingPieces(three, { random, ...options.groundAt ? { groundAt: options.groundAt } : {}, ...options.maxPieces ? { max: options.maxPieces } : {} });
|
|
2835
2835
|
const states = /* @__PURE__ */ new Map();
|
|
@@ -2838,9 +2838,9 @@ var createWounds = (three, options) => {
|
|
|
2838
2838
|
if (!found) throw new Error("game-characters-three: this body is not tracked by the wounds (call `track` first)");
|
|
2839
2839
|
return found;
|
|
2840
2840
|
};
|
|
2841
|
-
const burstBlood = (change, object) => {
|
|
2841
|
+
const burstBlood = (state, change, object, direction) => {
|
|
2842
2842
|
const point = worldPosition(three, object);
|
|
2843
|
-
bridge?.burst(change, [point.x, point.y, point.z]);
|
|
2843
|
+
bridge?.burst(change, [point.x, point.y, point.z], state.body.blood, direction ? [direction.x, direction.y, direction.z] : void 0);
|
|
2844
2844
|
};
|
|
2845
2845
|
const sideOf = (object, body) => new three.Vector3(object.position.x >= 0 ? 1 : -1, 0, 0).applyQuaternion(body.root.quaternion);
|
|
2846
2846
|
const hideBelow = (state, id) => {
|
|
@@ -2855,7 +2855,7 @@ var createWounds = (three, options) => {
|
|
|
2855
2855
|
}
|
|
2856
2856
|
const { node, radius, flip } = target;
|
|
2857
2857
|
const direction = sideOf(node, state.body);
|
|
2858
|
-
const fell = flying.launch(node,
|
|
2858
|
+
const fell = flying.launch(node, scene2, direction, (clone) => {
|
|
2859
2859
|
makeCut(three, materials, random, clone, radius, true);
|
|
2860
2860
|
}, id);
|
|
2861
2861
|
void fell;
|
|
@@ -2869,7 +2869,7 @@ var createWounds = (three, options) => {
|
|
|
2869
2869
|
for (let o = pendulum.node; o; o = o.parent) if (!o.visible) shown = false;
|
|
2870
2870
|
if (!shown) state.pendulums.splice(i, 1);
|
|
2871
2871
|
}
|
|
2872
|
-
burstBlood("severed", node);
|
|
2872
|
+
burstBlood(state, "severed", node, direction);
|
|
2873
2873
|
if (id === "head") state.dead = true;
|
|
2874
2874
|
};
|
|
2875
2875
|
const destroy = (state, id, direction) => {
|
|
@@ -2878,14 +2878,14 @@ var createWounds = (three, options) => {
|
|
|
2878
2878
|
const { node, radius, flip } = target;
|
|
2879
2879
|
const box = new three.Box3().setFromObject(node);
|
|
2880
2880
|
explodeChunks(three, materials, random, box, direction, (chunk, velocity, spin) => {
|
|
2881
|
-
|
|
2881
|
+
scene2.add(chunk);
|
|
2882
2882
|
flying.throwObject(chunk, velocity, spin, id);
|
|
2883
2883
|
});
|
|
2884
2884
|
node.visible = false;
|
|
2885
2885
|
makeStump(three, materials, random, node.parent, node.position, radius, flip);
|
|
2886
2886
|
state.gone.add(id);
|
|
2887
2887
|
hideBelow(state, id);
|
|
2888
|
-
burstBlood("destroyed", node);
|
|
2888
|
+
burstBlood(state, "destroyed", node, direction);
|
|
2889
2889
|
if (id === "head") state.dead = true;
|
|
2890
2890
|
};
|
|
2891
2891
|
const breakBone = (state, id) => {
|
|
@@ -2905,7 +2905,7 @@ var createWounds = (three, options) => {
|
|
|
2905
2905
|
const split = (state, direction) => {
|
|
2906
2906
|
const upper = state.body.rig.upper;
|
|
2907
2907
|
if (!upper.visible) return;
|
|
2908
|
-
flying.launch(upper,
|
|
2908
|
+
flying.launch(upper, scene2, direction, (clone) => {
|
|
2909
2909
|
makeCut(three, materials, random, clone, 0.16, false);
|
|
2910
2910
|
}, "torso");
|
|
2911
2911
|
upper.visible = false;
|
|
@@ -2913,7 +2913,7 @@ var createWounds = (three, options) => {
|
|
|
2913
2913
|
for (const id of ["torso", "neck", "head", "jaw", "upperArmL", "upperArmR", "forearmL", "forearmR", "handL", "handR"]) state.gone.add(id);
|
|
2914
2914
|
state.pendulums.length = 0;
|
|
2915
2915
|
state.dead = true;
|
|
2916
|
-
burstBlood("split", upper);
|
|
2916
|
+
burstBlood(state, "split", upper, direction);
|
|
2917
2917
|
};
|
|
2918
2918
|
const direction3 = (v, fallback) => v ? new three.Vector3(v[0], v[1], v[2]).normalize() : fallback;
|
|
2919
2919
|
return {
|
|
@@ -2947,6 +2947,10 @@ var createWounds = (three, options) => {
|
|
|
2947
2947
|
}
|
|
2948
2948
|
if (event.type !== "body") return;
|
|
2949
2949
|
const id = event.part;
|
|
2950
|
+
if (sound) {
|
|
2951
|
+
const heard = worldPosition(three, (id ? body.nodes[id]?.node : void 0) ?? body.root);
|
|
2952
|
+
sound.body(event, [heard.x, heard.y, heard.z]);
|
|
2953
|
+
}
|
|
2950
2954
|
const fallback = new three.Vector3(0, 0.2, 1);
|
|
2951
2955
|
const direction = direction3(event.exit, fallback);
|
|
2952
2956
|
switch (event.change) {
|
|
@@ -2973,8 +2977,10 @@ var createWounds = (three, options) => {
|
|
|
2973
2977
|
break;
|
|
2974
2978
|
case "wound": {
|
|
2975
2979
|
const target = id ? body.nodes[id] : void 0;
|
|
2980
|
+
const at = worldPosition(three, target?.node ?? body.root);
|
|
2981
|
+
bridge?.wound([at.x, at.y, at.z], body.blood, event.exit ? [direction.x, direction.y, direction.z] : void 0);
|
|
2976
2982
|
if (target && event.exit) gouge(three, materials, random, target.node, id.replace(/[LR]$/, ""), worldPosition(three, target.node), direction.clone().negate(), direction, 60, (chunk, velocity, spin) => {
|
|
2977
|
-
|
|
2983
|
+
scene2.add(chunk);
|
|
2978
2984
|
flying.throwObject(chunk, velocity, spin, id);
|
|
2979
2985
|
});
|
|
2980
2986
|
break;
|
|
@@ -3020,8 +3026,28 @@ var createWounds = (three, options) => {
|
|
|
3020
3026
|
};
|
|
3021
3027
|
|
|
3022
3028
|
// src/wounds/fluids-bridge.ts
|
|
3029
|
+
var colorOf = (hex2) => Number.parseInt(hex2.replace(/^#|^0x/i, ""), 16);
|
|
3030
|
+
var bloodLookOfAge = (age, options = {}) => ({
|
|
3031
|
+
...age.blood < (options.thinBelow ?? 0.3) ? { liquid: options.thinLiquid ?? "old_blood" } : {},
|
|
3032
|
+
tint: { fresh: colorOf(age.freshBloodColor), dry: colorOf(age.dryBloodColor) },
|
|
3033
|
+
amount: age.blood
|
|
3034
|
+
});
|
|
3023
3035
|
var BURST_LITERS = { severed: 0.35, destroyed: 0.6, split: 0.9 };
|
|
3024
|
-
var
|
|
3036
|
+
var BURST_DROPS = { severed: 4, destroyed: 7, split: 10 };
|
|
3037
|
+
var createFluidsBridge = ({ sink, film, spray, burstDrops = BURST_DROPS, liquid = "blood", spurtSpeed = 1.4 }) => {
|
|
3038
|
+
const throwDrops = (point, wanted, look, direction, speed, radius) => {
|
|
3039
|
+
const amount = look?.amount ?? 1;
|
|
3040
|
+
const count = amount <= 0 ? 0 : Math.max(1, Math.round(wanted * Math.min(1, amount)));
|
|
3041
|
+
return film && count > 0 ? film.splat({
|
|
3042
|
+
fluid: look?.liquid ?? liquid,
|
|
3043
|
+
point,
|
|
3044
|
+
count,
|
|
3045
|
+
...direction ? { direction } : {},
|
|
3046
|
+
...speed !== void 0 ? { speed } : {},
|
|
3047
|
+
...radius !== void 0 ? { radius } : {},
|
|
3048
|
+
...look?.tint ? { tint: { fresh: look.tint.fresh, dry: look.tint.dry } } : {}
|
|
3049
|
+
}) : 0;
|
|
3050
|
+
};
|
|
3025
3051
|
const bleed = (point, litersPerSecond, dt, direction = [0, -1, 0], pressurized = false) => {
|
|
3026
3052
|
const liters = litersPerSecond * dt;
|
|
3027
3053
|
if (liters <= 0) return;
|
|
@@ -3033,13 +3059,265 @@ var createFluidsBridge = ({ sink, liquid = "blood", spurtSpeed = 1.4 }) => {
|
|
|
3033
3059
|
handle(event) {
|
|
3034
3060
|
if (event.type === "leak" && event.fluid === "blood") bleed(event.point, event.rate, 1, event.direction, event.pressurized);
|
|
3035
3061
|
},
|
|
3036
|
-
burst
|
|
3062
|
+
burst(change, point, look, direction) {
|
|
3063
|
+
sink.splash(look?.liquid ?? liquid, point, BURST_LITERS[change]);
|
|
3064
|
+
throwDrops(point, burstDrops[change], look, direction);
|
|
3065
|
+
},
|
|
3066
|
+
wound: (point, look, direction) => throwDrops(point, spray?.count ?? 3, look, direction, spray?.speed, spray?.radius)
|
|
3037
3067
|
};
|
|
3038
3068
|
};
|
|
3039
3069
|
|
|
3040
3070
|
// src/test-area/module.ts
|
|
3041
3071
|
import { createCharacters, createRng as createRng3 } from "@adatechnology/game-characters";
|
|
3042
3072
|
|
|
3073
|
+
// src/audio/sound.ts
|
|
3074
|
+
import { GAME_SFX_BANK } from "@adatechnology/game-audio";
|
|
3075
|
+
|
|
3076
|
+
// src/audio/recipes.ts
|
|
3077
|
+
import { DISTANCE_CURVES, sceneNoise, sceneRecipe, sceneTone, sceneVoice } from "@adatechnology/game-audio";
|
|
3078
|
+
var UNDEAD_VOICE_GROUP = "undead_voice";
|
|
3079
|
+
var voiced = { bus: "voices", group: UNDEAD_VOICE_GROUP };
|
|
3080
|
+
var p = (param) => ({ param });
|
|
3081
|
+
var SCREAM_HOARSE = {
|
|
3082
|
+
id: "scream_hoarse",
|
|
3083
|
+
bus: "voices",
|
|
3084
|
+
order: "scream",
|
|
3085
|
+
duration: [1, 1.4],
|
|
3086
|
+
layers: [
|
|
3087
|
+
{
|
|
3088
|
+
source: { kind: "noise", offset: "random" },
|
|
3089
|
+
filters: {
|
|
3090
|
+
mode: "parallel",
|
|
3091
|
+
bank: [
|
|
3092
|
+
{ type: "bandpass", q: 6, frequency: { from: 700 * 1.15, to: 700 * 0.8, curve: "linear", over: "duration" }, level: 1 },
|
|
3093
|
+
{ type: "bandpass", q: 8, frequency: { from: 1150 * 1.15, to: 1150 * 0.8, curve: "linear", over: "duration" }, level: 0.7 },
|
|
3094
|
+
{ type: "bandpass", q: 10, frequency: { from: 2600 * 1.15, to: 2600 * 0.8, curve: "linear", over: "duration" }, level: 0.35 }
|
|
3095
|
+
]
|
|
3096
|
+
},
|
|
3097
|
+
modulators: [{ target: "gain", wave: "square", rate: [31, 40], depth: 0.35 }],
|
|
3098
|
+
envelope: [
|
|
3099
|
+
[0, 0, "set"],
|
|
3100
|
+
[0.06, 0.9, "linear"],
|
|
3101
|
+
["0.6d", 0.9, "set"],
|
|
3102
|
+
["1d", 1e-3, "exp", "fixed"]
|
|
3103
|
+
]
|
|
3104
|
+
}
|
|
3105
|
+
],
|
|
3106
|
+
falloff: DISTANCE_CURVES.game_scream,
|
|
3107
|
+
sources: ["jogo:L62128-62142 grito"]
|
|
3108
|
+
};
|
|
3109
|
+
var groanLayer = (base) => ({
|
|
3110
|
+
source: {
|
|
3111
|
+
kind: "osc",
|
|
3112
|
+
wave: "sawtooth",
|
|
3113
|
+
frequency: typeof base === "number" ? { from: base, to: base * 0.7, curve: "linear", over: "duration" } : { from: base, to: { ...base, scale: 0.7 }, curve: "linear", over: "duration" }
|
|
3114
|
+
},
|
|
3115
|
+
filters: { mode: "serial", bank: [{ type: "bandpass", q: 3, frequency: { set: typeof base === "number" ? base * 3 : { ...base, scale: 3 } } }] },
|
|
3116
|
+
envelope: [
|
|
3117
|
+
[0, 0, "set"],
|
|
3118
|
+
[0.15, 0.25, "linear"],
|
|
3119
|
+
["1d", 1e-3, "exp", "fixed"]
|
|
3120
|
+
]
|
|
3121
|
+
});
|
|
3122
|
+
var GROAN = {
|
|
3123
|
+
id: "groan",
|
|
3124
|
+
...voiced,
|
|
3125
|
+
order: "groan",
|
|
3126
|
+
vars: { base: [95, 135], dur: [0.9, 1.5] },
|
|
3127
|
+
duration: p("dur"),
|
|
3128
|
+
layers: [groanLayer(p("base"))],
|
|
3129
|
+
falloff: DISTANCE_CURVES.game_voice,
|
|
3130
|
+
sources: ["jogo:L62143-62155 voz gemido"]
|
|
3131
|
+
};
|
|
3132
|
+
var RUNNER_GROAN = {
|
|
3133
|
+
id: "runner_groan",
|
|
3134
|
+
...voiced,
|
|
3135
|
+
order: "groan",
|
|
3136
|
+
vars: { dur: [0.9, 1.5] },
|
|
3137
|
+
duration: p("dur"),
|
|
3138
|
+
layers: [groanLayer(300)],
|
|
3139
|
+
falloff: DISTANCE_CURVES.game_voice,
|
|
3140
|
+
sources: ["jogo:L62143-62155 voz corredor"]
|
|
3141
|
+
};
|
|
3142
|
+
var scene = (id, layers, line, options = {}) => sceneRecipe(id, layers, line, options);
|
|
3143
|
+
var groan = (id, vars, voice, line) => scene(id, [sceneVoice(voice)], line, { vars, ...voiced });
|
|
3144
|
+
var GROAN_WALKER = groan("groan_walker", { f0: [72, 107], d: [0.9, 2], fA: [480, 680], fB: [1050, 1350] }, { f0: p("f0"), f1: 0, d: p("d"), fA: p("fA"), fB: p("fB"), noise: 0.35, vol: 0.65 }, "L1345 groan (walker)");
|
|
3145
|
+
var GROAN_BRUTE = groan("groan_brute", { f0: [48, 62], d: [0.9, 2], fA: [480, 680], fB: [1050, 1350] }, { f0: p("f0"), f1: 0, d: p("d"), fA: p("fA"), fB: p("fB"), noise: 0.35, vol: 0.65 }, "L1345 groan (brute)");
|
|
3146
|
+
var GROAN_RUNNER = groan("groan_runner", { f0: [160, 250], d: [0.45, 0.85] }, { f0: p("f0"), f1: 0, d: p("d"), fA: 950, fB: 2300, noise: 0.6, vol: 0.5, wave: "square" }, "L1345 groan (runner)");
|
|
3147
|
+
var SCREAM = scene(
|
|
3148
|
+
"scream",
|
|
3149
|
+
[
|
|
3150
|
+
sceneVoice({ f0: 520, f1: 880, d: 2.6, fA: 1800, fB: 3200, noise: 0.9, vol: 1.6 }),
|
|
3151
|
+
sceneVoice({ f0: 640, f1: 1020, d: 2.4, fA: 2400, fB: 3800, noise: 0.6, vol: 1.1, wave: "square" })
|
|
3152
|
+
],
|
|
3153
|
+
"L1347 scream",
|
|
3154
|
+
{ bus: "voices" }
|
|
3155
|
+
);
|
|
3156
|
+
var INHALE = scene("inhale", [sceneNoise({ type: "bandpass", f: 1400, q: 1.5, a: 0.6, d: 0.9, vol: 0.5 })], "L1348 inhale", { bus: "voices" });
|
|
3157
|
+
var LEAP = scene("leap", [sceneVoice({ f0: 200, f1: 330, d: 0.5, fA: 1100, fB: 2500, noise: 0.7, vol: 0.6, wave: "square" }), sceneNoise({ type: "bandpass", f: 600, f2: 1600, d: 0.5, vol: 0.4 })], "L1349 leap", { bus: "voices" });
|
|
3158
|
+
var GRAB = scene("grab", [sceneNoise({ type: "bandpass", f: 500, q: 1, d: 0.25, vol: 0.7 }), sceneTone({ wave: "sawtooth", f: 90, f2: 60, d: 0.4, vol: 0.4 })], "L1350 grab", { bus: "voices" });
|
|
3159
|
+
var BITE = scene(
|
|
3160
|
+
"bite",
|
|
3161
|
+
[
|
|
3162
|
+
sceneNoise({ f: 1400, d: 0.2, vol: 1.1 }),
|
|
3163
|
+
...[0, 1, 2, 3].map((k) => sceneNoise({ type: "highpass", f: 2500, d: 0.03, vol: 0.5, delay: 0.03 + k * 0.04 })),
|
|
3164
|
+
sceneTone({ wave: "sawtooth", f: 320, f2: 170, d: 0.5, vol: 0.35, delay: 0.05 })
|
|
3165
|
+
],
|
|
3166
|
+
"L1351 bite",
|
|
3167
|
+
{ bus: "voices" }
|
|
3168
|
+
);
|
|
3169
|
+
var HURT = scene("hurt", [sceneTone({ wave: "sawtooth", f: 150, f2: 95, d: 0.22, vol: 0.3 }), sceneNoise({ f: 600, d: 0.1, vol: 0.4 })], "L1352 hurt", { bus: "voices" });
|
|
3170
|
+
var FLESH = scene("flesh", [sceneNoise({ f: 650, d: 0.08, vol: 0.6 }), sceneTone({ f: 90, f2: 45, d: 0.07, vol: 0.5 })], "L1353 flesh");
|
|
3171
|
+
var FLESH_HEAD = scene("flesh_head", [sceneNoise({ f: 1100, d: 0.12, vol: 0.9 }), sceneTone({ f: 130, f2: 45, d: 0.07, vol: 0.5 })], "L1353 flesh (head)");
|
|
3172
|
+
var SEVER = scene(
|
|
3173
|
+
"sever",
|
|
3174
|
+
[sceneNoise({ type: "bandpass", f: 500, q: 0.8, d: 0.3, vol: 1 }), sceneNoise({ type: "highpass", f: 2400, d: 0.06, vol: 0.7, delay: 0.02 }), sceneTone({ f: 110, f2: 40, d: 0.15, vol: 0.6 })],
|
|
3175
|
+
"L1355 sever"
|
|
3176
|
+
);
|
|
3177
|
+
var GORE = scene(
|
|
3178
|
+
"gore",
|
|
3179
|
+
[
|
|
3180
|
+
sceneNoise({ f: 900, f2: 200, d: 0.4, vol: 1.1, wet: 0.3 }),
|
|
3181
|
+
...[0, 1, 2].map((k) => sceneNoise({ type: "bandpass", f: [400, 800], q: 2, d: 0.08, vol: 0.5, delay: 0.08 + k * 0.07 }))
|
|
3182
|
+
],
|
|
3183
|
+
"L1356 gore"
|
|
3184
|
+
);
|
|
3185
|
+
var CHARACTER_SOUNDS = [
|
|
3186
|
+
SCREAM_HOARSE,
|
|
3187
|
+
GROAN,
|
|
3188
|
+
RUNNER_GROAN,
|
|
3189
|
+
GROAN_WALKER,
|
|
3190
|
+
GROAN_BRUTE,
|
|
3191
|
+
GROAN_RUNNER,
|
|
3192
|
+
SCREAM,
|
|
3193
|
+
INHALE,
|
|
3194
|
+
LEAP,
|
|
3195
|
+
GRAB,
|
|
3196
|
+
BITE,
|
|
3197
|
+
HURT,
|
|
3198
|
+
FLESH,
|
|
3199
|
+
FLESH_HEAD,
|
|
3200
|
+
SEVER,
|
|
3201
|
+
GORE
|
|
3202
|
+
];
|
|
3203
|
+
var CHARACTER_SOUND_BANK = { id: "characters_three", sounds: CHARACTER_SOUNDS };
|
|
3204
|
+
|
|
3205
|
+
// src/audio/voices.json
|
|
3206
|
+
var voices_default = {
|
|
3207
|
+
sources: [
|
|
3208
|
+
"cena:L1345 groan(z): the Brutamonte has its own, a chase speed above 3.4 is a runner's, every other body groans like a walker",
|
|
3209
|
+
"cena:L1238, L1248, L1249 the scream and the inhale belong to the Gritador, the leap to the Saltador",
|
|
3210
|
+
"cena:L1226 the groan when a body wakes (FEIGN to CHASE) and the game L67275/L67328 voz('corredor') when one notices the player",
|
|
3211
|
+
"jogo:L66986 the groan of the game: 'corredor' for the runner category, 'gemido' for the rest; jogo:L66989 screamAt plays 'grito'",
|
|
3212
|
+
"types.json: the voice line of each Bestiario type"
|
|
3213
|
+
],
|
|
3214
|
+
types: {
|
|
3215
|
+
walker: { origin: "team", groan: "groan_walker", gameGroan: "groan" },
|
|
3216
|
+
runner: { origin: "team", groan: "groan_runner", gameGroan: "runner_groan" },
|
|
3217
|
+
crawler: { origin: "rule", groan: "groan_walker", gameGroan: "groan" },
|
|
3218
|
+
brute: { origin: "team", groan: "groan_brute", gameGroan: "groan" },
|
|
3219
|
+
sniffer: {
|
|
3220
|
+
origin: "extra",
|
|
3221
|
+
groan: "groan_runner",
|
|
3222
|
+
gameGroan: "groan",
|
|
3223
|
+
idle: [{ sound: "inhale" }, { sound: "groan_runner", delay: 0.6 }]
|
|
3224
|
+
},
|
|
3225
|
+
leaper: { origin: "team", groan: "groan_runner", gameGroan: "groan", special: [{ sound: "leap" }] },
|
|
3226
|
+
bloater: { origin: "rule", groan: "groan_walker", gameGroan: "groan" },
|
|
3227
|
+
stalker: { origin: "rule", groan: "groan_runner", gameGroan: "groan", idle: [] },
|
|
3228
|
+
blind: { origin: "rule", groan: "groan_walker", gameGroan: "groan" },
|
|
3229
|
+
screamer: {
|
|
3230
|
+
origin: "team",
|
|
3231
|
+
groan: "groan_walker",
|
|
3232
|
+
gameGroan: "groan",
|
|
3233
|
+
special: [{ sound: "inhale" }, { sound: "scream", delay: 1.5 }],
|
|
3234
|
+
gameSpecial: [{ sound: "scream_hoarse" }]
|
|
3235
|
+
},
|
|
3236
|
+
charred: { origin: "rule", groan: "groan_walker", gameGroan: "groan" },
|
|
3237
|
+
drowned: { origin: "rule", groan: "groan_walker", gameGroan: "groan" },
|
|
3238
|
+
devourer: {
|
|
3239
|
+
origin: "extra",
|
|
3240
|
+
groan: "groan_walker",
|
|
3241
|
+
gameGroan: "groan",
|
|
3242
|
+
idle: [{ sound: "groan_walker" }, { sound: "bite", delay: 0.4 }]
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
};
|
|
3246
|
+
|
|
3247
|
+
// src/audio/sound.ts
|
|
3248
|
+
var DATA = voices_default;
|
|
3249
|
+
var UNDEAD_VOICES = DATA.types;
|
|
3250
|
+
var UNDEAD_VOICE_SOURCES = DATA.sources;
|
|
3251
|
+
var BODY_SOUNDS = {
|
|
3252
|
+
art_scene: {
|
|
3253
|
+
wound: { sound: "flesh", head: "flesh_head" },
|
|
3254
|
+
severed: { sound: "sever" },
|
|
3255
|
+
destroyed: { sound: "gore" },
|
|
3256
|
+
split: { sound: "gore" }
|
|
3257
|
+
},
|
|
3258
|
+
game: {
|
|
3259
|
+
wound: { sound: "sfx_flesh" }
|
|
3260
|
+
}
|
|
3261
|
+
};
|
|
3262
|
+
var PLAYER_SOUNDS = { grab: "grab", bite: "bite", hurt: "hurt" };
|
|
3263
|
+
var HEAD_PARTS = /* @__PURE__ */ new Set(["head", "jaw"]);
|
|
3264
|
+
function createCharacterSound(sink, options = {}) {
|
|
3265
|
+
sink.register?.(CHARACTER_SOUND_BANK);
|
|
3266
|
+
sink.register?.(GAME_SFX_BANK);
|
|
3267
|
+
const style = options.style ?? "art_scene";
|
|
3268
|
+
const voices = { ...UNDEAD_VOICES, ...options.voices };
|
|
3269
|
+
const rules = { ...BODY_SOUNDS.art_scene, ...style === "game" ? BODY_SOUNDS.game : {}, ...options.body };
|
|
3270
|
+
const queue = [];
|
|
3271
|
+
const voiceOf = (typeId) => {
|
|
3272
|
+
const own = voices[typeId];
|
|
3273
|
+
if (own === null) return void 0;
|
|
3274
|
+
return own ?? (options.fallbackType ? voices[options.fallbackType] ?? void 0 : void 0);
|
|
3275
|
+
};
|
|
3276
|
+
const stepsOf = (typeId, moment) => {
|
|
3277
|
+
const voice = voiceOf(typeId);
|
|
3278
|
+
if (!voice) return [];
|
|
3279
|
+
const groan2 = style === "game" ? voice.gameGroan : voice.groan;
|
|
3280
|
+
if (moment === "special") return (style === "game" ? voice.gameSpecial ?? voice.special : voice.special) ?? [];
|
|
3281
|
+
if (moment === "alert") return voice.alert ?? [{ sound: style === "game" ? "runner_groan" : groan2 }];
|
|
3282
|
+
return voice.idle ?? [{ sound: groan2 }];
|
|
3283
|
+
};
|
|
3284
|
+
const start = (sound, at, volume) => void sink.play({ sound, position: at, volume });
|
|
3285
|
+
return {
|
|
3286
|
+
stepsOf,
|
|
3287
|
+
voice(typeId, moment, position, volume = 1) {
|
|
3288
|
+
const steps = stepsOf(typeId, moment);
|
|
3289
|
+
for (const step of steps) {
|
|
3290
|
+
if (step.delay && step.delay > 0) queue.push({ sound: step.sound, at: position, volume, wait: step.delay });
|
|
3291
|
+
else start(step.sound, position, volume);
|
|
3292
|
+
}
|
|
3293
|
+
return steps.length;
|
|
3294
|
+
},
|
|
3295
|
+
body(event, position, volume = 1) {
|
|
3296
|
+
if (event.type !== "body") return false;
|
|
3297
|
+
const rule = rules[event.change];
|
|
3298
|
+
if (!rule) return false;
|
|
3299
|
+
const head = event.part !== void 0 && HEAD_PARTS.has(event.part);
|
|
3300
|
+
start(head && rule.head ? rule.head : rule.sound, position, volume);
|
|
3301
|
+
return true;
|
|
3302
|
+
},
|
|
3303
|
+
player: (kind, volume = 1) => void sink.play({ sound: PLAYER_SOUNDS[kind], volume }),
|
|
3304
|
+
update(dt) {
|
|
3305
|
+
for (let i = queue.length - 1; i >= 0; i--) {
|
|
3306
|
+
const item = queue[i];
|
|
3307
|
+
item.wait -= dt;
|
|
3308
|
+
if (item.wait <= 0) {
|
|
3309
|
+
queue.splice(i, 1);
|
|
3310
|
+
start(item.sound, item.at, item.volume);
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
},
|
|
3314
|
+
pending: () => queue.length,
|
|
3315
|
+
dispose() {
|
|
3316
|
+
queue.length = 0;
|
|
3317
|
+
}
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3043
3321
|
// src/test-area/crowd.ts
|
|
3044
3322
|
import { encodePoseHeight } from "@adatechnology/game-characters";
|
|
3045
3323
|
var FIELD = { x0: 12, x1: 30, z0: -16, z1: 16 };
|
|
@@ -3306,7 +3584,7 @@ var TEXT = {
|
|
|
3306
3584
|
hit_preset: "What the Fire hit button does to the body you look at",
|
|
3307
3585
|
protection: "Worn by the bodies created from now on"
|
|
3308
3586
|
},
|
|
3309
|
-
actions: { heal: "Heal the aimed body", kill: "Kill the aimed body", infect: "Bite the aimed person (accelerated)", spawn_100: "Spawn 100 in the crowd", spawn_1000: "Spawn 1000 in the crowd", fire_preset: "Fire the hit" },
|
|
3587
|
+
actions: { heal: "Heal the aimed body", kill: "Kill the aimed body", infect: "Bite the aimed person (accelerated)", spawn_100: "Spawn 100 in the crowd", spawn_1000: "Spawn 1000 in the crowd", fire_preset: "Fire the hit", voice: "Make the aimed zombie speak (groan, by the moment)", special_call: "Make the aimed zombie use its call (scream, leap)" },
|
|
3310
3588
|
options: {
|
|
3311
3589
|
penetration: { physical: "Physical", "team-game": "Team game" },
|
|
3312
3590
|
death_rule: { "vitality-or-head": "Vitality or head", "head-only": "Head only" },
|
|
@@ -3351,7 +3629,7 @@ var TEXT = {
|
|
|
3351
3629
|
hit_preset: "O que o bot\xE3o Disparar faz no corpo para o qual voc\xEA olha",
|
|
3352
3630
|
protection: "Vestida pelos corpos criados daqui em diante"
|
|
3353
3631
|
},
|
|
3354
|
-
actions: { heal: "Curar o corpo mirado", kill: "Matar o corpo mirado", infect: "Morder a pessoa mirada (acelerado)", spawn_100: "P\xF4r 100 na multid\xE3o", spawn_1000: "P\xF4r 1000 na multid\xE3o", fire_preset: "Disparar o golpe" },
|
|
3632
|
+
actions: { heal: "Curar o corpo mirado", kill: "Matar o corpo mirado", infect: "Morder a pessoa mirada (acelerado)", spawn_100: "P\xF4r 100 na multid\xE3o", spawn_1000: "P\xF4r 1000 na multid\xE3o", fire_preset: "Disparar o golpe", voice: "Fazer o zumbi mirado falar (gemido, pelo momento)", special_call: "Fazer o zumbi mirado usar o chamado (grito, salto)" },
|
|
3355
3633
|
options: {
|
|
3356
3634
|
penetration: { physical: "F\xEDsica", "team-game": "Do jogo" },
|
|
3357
3635
|
death_rule: { "vitality-or-head": "Vitalidade ou cabe\xE7a", "head-only": "S\xF3 a cabe\xE7a" },
|
|
@@ -3406,6 +3684,7 @@ function createCharactersTestModule(options = {}) {
|
|
|
3406
3684
|
let best = null;
|
|
3407
3685
|
let people = null;
|
|
3408
3686
|
let wounds = null;
|
|
3687
|
+
let sound = null;
|
|
3409
3688
|
let crowd = null;
|
|
3410
3689
|
let selected = null;
|
|
3411
3690
|
let time = 0;
|
|
@@ -3498,6 +3777,9 @@ function createCharactersTestModule(options = {}) {
|
|
|
3498
3777
|
if (Math.abs(scale - 1) > 0.01) object.scale.setScalar(scale);
|
|
3499
3778
|
const entry = { id, kind: made.kind, cardId: made.cardId, root: object, body: woundBodyOf(object), eyes: three ? findEyes(three, object) : null, built: made.built, character, walk: 0, tilt: 0 };
|
|
3500
3779
|
entry.character = wrap(entry, character);
|
|
3780
|
+
const age = made.kind === "zombie" ? undead?.traitsOf(character)?.age : void 0;
|
|
3781
|
+
const blood = age !== void 0 ? options.bloodOf?.(age) : void 0;
|
|
3782
|
+
if (entry.body && blood) entry.body.blood = blood;
|
|
3501
3783
|
entries.set(id, entry);
|
|
3502
3784
|
selected ?? (selected = entry);
|
|
3503
3785
|
trackEntry(entry);
|
|
@@ -3580,8 +3862,8 @@ function createCharactersTestModule(options = {}) {
|
|
|
3580
3862
|
const selectedOrFirst = () => selected ?? entries.values().next().value ?? null;
|
|
3581
3863
|
const fireOn = (entry) => {
|
|
3582
3864
|
const preset2 = hitPresetOf(settings.hit);
|
|
3583
|
-
const
|
|
3584
|
-
entry.character.hit(hitFor(preset2, [
|
|
3865
|
+
const p2 = entry.root.position;
|
|
3866
|
+
entry.character.hit(hitFor(preset2, [p2.x, p2.y, p2.z], 1.3, kit.calibers));
|
|
3585
3867
|
};
|
|
3586
3868
|
const ensureCrowd = () => {
|
|
3587
3869
|
if (crowd) return crowd;
|
|
@@ -3616,7 +3898,14 @@ function createCharactersTestModule(options = {}) {
|
|
|
3616
3898
|
if (infection.reanimated) target.character = wrap(target, undead.turn(target.character));
|
|
3617
3899
|
}
|
|
3618
3900
|
} else if (change.id === "fire_preset" && target) fireOn(target);
|
|
3619
|
-
else if (change.id === "
|
|
3901
|
+
else if ((change.id === "voice" || change.id === "special_call") && sound) {
|
|
3902
|
+
const speaker = target?.kind === "zombie" ? target : [...entries.values()].find((entry) => entry.kind === "zombie");
|
|
3903
|
+
if (speaker) {
|
|
3904
|
+
const at = speaker.root.getWorldPosition(new host.THREE.Vector3());
|
|
3905
|
+
const hurried = settings.moment === "alert" || settings.moment === "chase" || settings.moment === "sprint";
|
|
3906
|
+
sound.voice(speaker.cardId, change.id === "special_call" ? "special" : hurried ? "alert" : "idle", [at.x, at.y + 1.5, at.z]);
|
|
3907
|
+
}
|
|
3908
|
+
} else if (change.id === "spawn_100" || change.id === "spawn_1000") {
|
|
3620
3909
|
ensureBuilders(host?.THREE ?? three);
|
|
3621
3910
|
ensureCrowd().spawn(change.id === "spawn_100" ? 100 : 1e3);
|
|
3622
3911
|
}
|
|
@@ -3721,6 +4010,8 @@ function createCharactersTestModule(options = {}) {
|
|
|
3721
4010
|
withHint(choice("hit_preset", HIT_PRESETS.map((preset2) => preset2.id), "9x19"), text.hints.hit_preset),
|
|
3722
4011
|
withHint(choice("protection", ["none", "vest_police", "vest_military", "helmet_combat", "turnout_gear", "vest_helmet"], "none"), text.hints.protection),
|
|
3723
4012
|
{ kind: "action", id: "fire_preset", name: text.actions.fire_preset },
|
|
4013
|
+
{ kind: "action", id: "voice", name: text.actions.voice },
|
|
4014
|
+
{ kind: "action", id: "special_call", name: text.actions.special_call },
|
|
3724
4015
|
{ kind: "action", id: "heal", name: text.actions.heal },
|
|
3725
4016
|
{ kind: "action", id: "kill", name: text.actions.kill },
|
|
3726
4017
|
{ kind: "action", id: "infect", name: text.actions.infect },
|
|
@@ -3748,9 +4039,10 @@ function createCharactersTestModule(options = {}) {
|
|
|
3748
4039
|
attach(lent) {
|
|
3749
4040
|
host = lent;
|
|
3750
4041
|
ensureBuilders(lent.THREE);
|
|
3751
|
-
const
|
|
3752
|
-
const bridge = options.fluids ? createFluidsBridge({ sink: options.fluids }) : void 0;
|
|
3753
|
-
|
|
4042
|
+
const scene2 = lent.scene;
|
|
4043
|
+
const bridge = options.fluids ? createFluidsBridge({ sink: options.fluids, ...options.film ? { film: options.film } : {} }) : void 0;
|
|
4044
|
+
sound = lent.audio && options.sound !== false ? createCharacterSound(lent.audio, options.sound ?? {}) : null;
|
|
4045
|
+
wounds = createWounds(lent.THREE, { random, scene: scene2, groundAt: (x, z) => lent.groundAt(x, z), ...bridge ? { bridge } : {}, ...sound ? { sound } : {} });
|
|
3754
4046
|
for (const entry of entries.values()) trackEntry(entry);
|
|
3755
4047
|
const stopChange = lent.bench?.onChange(onChange);
|
|
3756
4048
|
const stopFrame = lent.onFrame((dt) => {
|
|
@@ -3763,6 +4055,7 @@ function createCharactersTestModule(options = {}) {
|
|
|
3763
4055
|
animate(entry, dt);
|
|
3764
4056
|
}
|
|
3765
4057
|
wounds?.update(dt);
|
|
4058
|
+
sound?.update(dt);
|
|
3766
4059
|
crowd?.update(dt, lent.camera);
|
|
3767
4060
|
});
|
|
3768
4061
|
return () => {
|
|
@@ -3771,6 +4064,8 @@ function createCharactersTestModule(options = {}) {
|
|
|
3771
4064
|
clearOverlays();
|
|
3772
4065
|
wounds?.dispose();
|
|
3773
4066
|
wounds = null;
|
|
4067
|
+
sound?.dispose();
|
|
4068
|
+
sound = null;
|
|
3774
4069
|
if (crowd) {
|
|
3775
4070
|
crowd.group.parent?.remove(crowd.group);
|
|
3776
4071
|
crowd = null;
|
|
@@ -3804,7 +4099,10 @@ export {
|
|
|
3804
4099
|
BAKE_NEUTRAL_POSE,
|
|
3805
4100
|
BODY_CHUNK,
|
|
3806
4101
|
BODY_MATERIAL_DEF,
|
|
4102
|
+
BODY_SOUNDS,
|
|
3807
4103
|
CHARACTERS_AREA_ID,
|
|
4104
|
+
CHARACTER_SOUNDS,
|
|
4105
|
+
CHARACTER_SOUND_BANK,
|
|
3808
4106
|
EXTRAS,
|
|
3809
4107
|
FACE_FEATURES,
|
|
3810
4108
|
HAND_ITEMS,
|
|
@@ -3817,16 +4115,22 @@ export {
|
|
|
3817
4115
|
LOD_SEGMENTS,
|
|
3818
4116
|
PENDULUM,
|
|
3819
4117
|
PERSON_ITEM_PREFIX,
|
|
4118
|
+
PLAYER_SOUNDS,
|
|
4119
|
+
UNDEAD_VOICES,
|
|
4120
|
+
UNDEAD_VOICE_GROUP,
|
|
4121
|
+
UNDEAD_VOICE_SOURCES,
|
|
3820
4122
|
ZOMBIE_ITEM_PREFIX,
|
|
3821
4123
|
bakeBody,
|
|
3822
4124
|
bakeVariants,
|
|
3823
4125
|
blobGeometry,
|
|
3824
4126
|
blobRadii,
|
|
4127
|
+
bloodLookOfAge,
|
|
3825
4128
|
buildInstancedBody,
|
|
3826
4129
|
createBestBuilder,
|
|
3827
4130
|
createBodyBatch,
|
|
3828
4131
|
createBodyChunk,
|
|
3829
4132
|
createBodyMaterial,
|
|
4133
|
+
createCharacterSound,
|
|
3830
4134
|
createCharactersTestModule,
|
|
3831
4135
|
createEye,
|
|
3832
4136
|
createFluidsBridge,
|