@adatechnology/game-destruction-three 0.12.1 → 0.13.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 +10 -2
- package/dist/index.global.js +651 -153
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +65 -14
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.global.js
CHANGED
|
@@ -55,6 +55,39 @@ var GameDestructionThree = (() => {
|
|
|
55
55
|
const length = Math.hypot(direction[0], direction[1], direction[2]);
|
|
56
56
|
return { speed, direction: [direction[0] / length, direction[1] / length, direction[2] / length], distance, falloff };
|
|
57
57
|
}
|
|
58
|
+
var DEFAULT_COST_ALLOCATION_BYTES = 1024 * 1024;
|
|
59
|
+
var DEFERRED_QUEUE_ID = {
|
|
60
|
+
DESTRUCTION_EVENTS: "destruction.events",
|
|
61
|
+
BREACH_PAINT: "floorplan.breach-paint",
|
|
62
|
+
AUDIO_VOICES: "audio.voices",
|
|
63
|
+
VEHICLE_CREASE: "vehicles.crease"
|
|
64
|
+
};
|
|
65
|
+
var DEFERRED_PRIORITY = {
|
|
66
|
+
[DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS]: { priority: 300, phase: "present" },
|
|
67
|
+
[DEFERRED_QUEUE_ID.BREACH_PAINT]: { priority: 250, phase: "present" },
|
|
68
|
+
[DEFERRED_QUEUE_ID.AUDIO_VOICES]: { priority: 200, phase: "idle", reserveMs: 1 },
|
|
69
|
+
[DEFERRED_QUEUE_ID.VEHICLE_CREASE]: { priority: 100, phase: "idle" }
|
|
70
|
+
};
|
|
71
|
+
var WARNED_KEY = /* @__PURE__ */ Symbol.for("@adatechnology/game-contracts.warned");
|
|
72
|
+
var warnedKeys = () => {
|
|
73
|
+
const holder = globalThis;
|
|
74
|
+
return holder[WARNED_KEY] ?? (holder[WARNED_KEY] = /* @__PURE__ */ new Set());
|
|
75
|
+
};
|
|
76
|
+
var isProduction = () => {
|
|
77
|
+
try {
|
|
78
|
+
return process.env.NODE_ENV === "production";
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var isQuiet = () => globalThis.__GAME_KIT_QUIET__ === true;
|
|
84
|
+
var warnOnce = (key, message) => {
|
|
85
|
+
if (isQuiet() || isProduction()) return;
|
|
86
|
+
const seen = warnedKeys();
|
|
87
|
+
if (seen.has(key)) return;
|
|
88
|
+
seen.add(key);
|
|
89
|
+
console.warn(`[game-kit] ${message}`);
|
|
90
|
+
};
|
|
58
91
|
var KIT_ORIGIN_KEY = "kitOrigin";
|
|
59
92
|
function markKitOrigin(object, origin) {
|
|
60
93
|
const node = object;
|
|
@@ -69,6 +102,43 @@ var GameDestructionThree = (() => {
|
|
|
69
102
|
}
|
|
70
103
|
return null;
|
|
71
104
|
}
|
|
105
|
+
var SURFACE_IDS = ["concrete", "asphalt", "wood", "tile", "dirt", "grass", "gravel", "sand", "metal", "water", "glass", "carpet"];
|
|
106
|
+
var GAIT_IDS = ["walk", "run"];
|
|
107
|
+
var LANDING_IDS = ["light", "heavy"];
|
|
108
|
+
var BODY_SOUND_IDS = [
|
|
109
|
+
"jump",
|
|
110
|
+
"cloth_rustle",
|
|
111
|
+
"body_fall",
|
|
112
|
+
"fall_damage",
|
|
113
|
+
"player_hurt",
|
|
114
|
+
"player_breath_run",
|
|
115
|
+
"player_death",
|
|
116
|
+
"bite",
|
|
117
|
+
"grab",
|
|
118
|
+
"punch_swing",
|
|
119
|
+
"punch_hit_body",
|
|
120
|
+
"punch_hit_face",
|
|
121
|
+
"punch_hit_object",
|
|
122
|
+
"kick_swing",
|
|
123
|
+
"kick_hit",
|
|
124
|
+
"kick_hit_object",
|
|
125
|
+
"shove_two_hands",
|
|
126
|
+
"knife_swing",
|
|
127
|
+
"knife_stab",
|
|
128
|
+
"knife_slash",
|
|
129
|
+
"knife_hit_object",
|
|
130
|
+
"blunt_swing",
|
|
131
|
+
"blunt_hit",
|
|
132
|
+
"blunt_hit_object",
|
|
133
|
+
"axe_swing",
|
|
134
|
+
"axe_chop",
|
|
135
|
+
"axe_hit_object"
|
|
136
|
+
];
|
|
137
|
+
var footstepSoundId = (surface, gait) => `footstep_${surface}_${gait}`;
|
|
138
|
+
var FOOTSTEP_SOUND_IDS = SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstepSoundId(surface, gait)));
|
|
139
|
+
var LAND_SOUND_IDS = SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((landing2) => `land_${surface}_${landing2}`));
|
|
140
|
+
var BODY_FOLEY_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
|
|
141
|
+
var SWING_LEAD_SECONDS = { punch: 0.07, kick: 0.09, shove: 0, blade: 0.05, blunt: 0.1, chop: 0.11 };
|
|
72
142
|
|
|
73
143
|
// ../game-vfx/dist/index.js
|
|
74
144
|
var __defProp2 = Object.defineProperty;
|
|
@@ -2564,22 +2634,22 @@ var GameDestructionThree = (() => {
|
|
|
2564
2634
|
)
|
|
2565
2635
|
]
|
|
2566
2636
|
};
|
|
2567
|
-
function normalized(
|
|
2568
|
-
const length = Math.sqrt(
|
|
2637
|
+
function normalized(vector2) {
|
|
2638
|
+
const length = Math.sqrt(vector2[0] * vector2[0] + vector2[1] * vector2[1] + vector2[2] * vector2[2]) || 1;
|
|
2569
2639
|
const inverse = 1 / length;
|
|
2570
|
-
return [
|
|
2640
|
+
return [vector2[0] * inverse, vector2[1] * inverse, vector2[2] * inverse];
|
|
2571
2641
|
}
|
|
2572
|
-
function plus(
|
|
2573
|
-
return [
|
|
2642
|
+
function plus(vector2, x, y, z) {
|
|
2643
|
+
return [vector2[0] + x, vector2[1] + y, vector2[2] + z];
|
|
2574
2644
|
}
|
|
2575
|
-
function scaledBy(
|
|
2576
|
-
return [
|
|
2645
|
+
function scaledBy(vector2, factor) {
|
|
2646
|
+
return [vector2[0] * factor, vector2[1] * factor, vector2[2] * factor];
|
|
2577
2647
|
}
|
|
2578
|
-
function plusScaled(
|
|
2579
|
-
return [
|
|
2648
|
+
function plusScaled(vector2, direction, factor) {
|
|
2649
|
+
return [vector2[0] + direction[0] * factor, vector2[1] + direction[1] * factor, vector2[2] + direction[2] * factor];
|
|
2580
2650
|
}
|
|
2581
|
-
function negated(
|
|
2582
|
-
return [-
|
|
2651
|
+
function negated(vector2) {
|
|
2652
|
+
return [-vector2[0], -vector2[1], -vector2[2]];
|
|
2583
2653
|
}
|
|
2584
2654
|
var FRAME_DUST = normalized([0, -0.3, 1]);
|
|
2585
2655
|
var SPLINTER_DIRECTION = normalized([0, 0.3, 1]);
|
|
@@ -3882,9 +3952,32 @@ var GameDestructionThree = (() => {
|
|
|
3882
3952
|
/** The leak caught fire (an incendiary or tracer round, a spark, a fire near): the page's whole sequence, the fire running along the pool to the tank and the blast. */
|
|
3883
3953
|
aliasOf(FUEL_LEAK, "vehicle_leak_ignited", {})
|
|
3884
3954
|
];
|
|
3955
|
+
var vector = [variable("vx"), variable("vy"), variable("vz")];
|
|
3956
|
+
var range = (name) => [variable(name), variable(name)];
|
|
3957
|
+
var singleParticle = (id, system) => ({
|
|
3958
|
+
id,
|
|
3959
|
+
pageId: `particle:${id}`,
|
|
3960
|
+
team: "world",
|
|
3961
|
+
budget: { particles: 1, lights: 0 },
|
|
3962
|
+
loop: 1e-3,
|
|
3963
|
+
timeline: [
|
|
3964
|
+
spawn(system, {
|
|
3965
|
+
p: [0, 0, 0],
|
|
3966
|
+
v: vector,
|
|
3967
|
+
life: range("life"),
|
|
3968
|
+
size: range("size"),
|
|
3969
|
+
grow: variable("grow"),
|
|
3970
|
+
c0: variable("color"),
|
|
3971
|
+
a: variable("alpha"),
|
|
3972
|
+
g: variable("gravity"),
|
|
3973
|
+
drag: variable("drag")
|
|
3974
|
+
})
|
|
3975
|
+
]
|
|
3976
|
+
});
|
|
3977
|
+
var SINGLE_PARTICLE_EFFECTS = [singleParticle("particle_puff", "normal"), singleParticle("particle_glow", "add"), singleParticle("particle_streak", "streak")];
|
|
3885
3978
|
var ALL_EFFECTS = [BLOOD_HIT, HEADSHOT, DISMEMBER, BLOATER_CLOUD, SCREAMER_SHRIEK, CORPSE_DECAY, REANIMATION, EYES_IN_FOG, MUZZLE_FLASH, IMPACT_BY_MATERIAL, PENETRATION, CASINGS_AND_MAGAZINE, MOLOTOV, GRENADE, EXPLOSIVE_BARREL, SHOPFRONT_GLASS, DOOR_STAGES, BUILDING_COLLAPSE, BLOCK_WALL, METAL_DOOR_EFFECT, IRON_RAILING, WOODEN_FENCE, HIT_POINT_BREAKAGE, PRESSURE_CYLINDERS, BUILDING_FIRE, RAIN_AND_PUDDLES, FOG_AND_ASH, SCRAPING_SPARKS, ENGINE_SMOKE, FUEL_LEAK, CAR_EXPLOSION, WINDSHIELD, HEADLIGHTS_IN_RAIN, DAMAGE_AND_BITE, INFECTION_PHASES, PANIC, BLOATER_BLINDNESS, CAMPFIRE, COUGH_AND_BREATH];
|
|
3886
3979
|
var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
|
|
3887
|
-
var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS].map((effect) => [effect.id, effect])) };
|
|
3980
|
+
var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS, ...SINGLE_PARTICLE_EFFECTS].map((effect) => [effect.id, effect])) };
|
|
3888
3981
|
var TABLE = {
|
|
3889
3982
|
concrete: { dust: 9078138, chip: 7236195, spark: 2 },
|
|
3890
3983
|
metal: { dust: 0, chip: 0, spark: 18 },
|
|
@@ -4554,8 +4647,8 @@ var GameDestructionThree = (() => {
|
|
|
4554
4647
|
var UP12 = [0, 1, 0];
|
|
4555
4648
|
var ZERO = [0, 0, 0];
|
|
4556
4649
|
var FULL_TURN_DRAWN = 6.283;
|
|
4557
|
-
function scaledSizes(
|
|
4558
|
-
return [
|
|
4650
|
+
function scaledSizes(range2, factor) {
|
|
4651
|
+
return [range2[0] * Math.sqrt(factor), range2[1] * Math.sqrt(factor)];
|
|
4559
4652
|
}
|
|
4560
4653
|
function fireEmit(engine, key, dt, centre, radius, factor, options = {}) {
|
|
4561
4654
|
const wind = options.wind ?? ZERO;
|
|
@@ -6409,9 +6502,9 @@ var GameDestructionThree = (() => {
|
|
|
6409
6502
|
var TWO_PI_ROUNDED = 6.2832;
|
|
6410
6503
|
var WIDE_CONE = 3.1;
|
|
6411
6504
|
var UP13 = [0, 1, 0];
|
|
6412
|
-
function drawRange(random,
|
|
6413
|
-
if (Array.isArray(
|
|
6414
|
-
return
|
|
6505
|
+
function drawRange(random, range2) {
|
|
6506
|
+
if (Array.isArray(range2)) return random.between(range2[0], range2[1]);
|
|
6507
|
+
return range2 || 0;
|
|
6415
6508
|
}
|
|
6416
6509
|
function randomUnit(random, out) {
|
|
6417
6510
|
const up = random.next() * 2 - 1;
|
|
@@ -7178,20 +7271,20 @@ var GameDestructionThree = (() => {
|
|
|
7178
7271
|
return [origin[0] + cos2 * x + sin2 * z, origin[1] + point[1] - anchor[1], origin[2] - sin2 * x + cos2 * z];
|
|
7179
7272
|
}
|
|
7180
7273
|
/** A direction or velocity of the recipe's space in the world. */
|
|
7181
|
-
turn(
|
|
7274
|
+
turn(vector2) {
|
|
7182
7275
|
const placement = this.placement;
|
|
7183
|
-
if (!placement) return
|
|
7184
|
-
if (placement.orientation) return rotate(placement.orientation,
|
|
7276
|
+
if (!placement) return vector2;
|
|
7277
|
+
if (placement.orientation) return rotate(placement.orientation, vector2);
|
|
7185
7278
|
const cos2 = Math.cos(placement.yaw);
|
|
7186
7279
|
const sin2 = Math.sin(placement.yaw);
|
|
7187
|
-
return [cos2 *
|
|
7280
|
+
return [cos2 * vector2[0] + sin2 * vector2[2], vector2[1], -sin2 * vector2[0] + cos2 * vector2[2]];
|
|
7188
7281
|
}
|
|
7189
7282
|
placed(options) {
|
|
7190
7283
|
if (!this.placement) return options;
|
|
7191
7284
|
return { ...options, p: this.place(options.p), ...options.dir ? { dir: this.turn(options.dir) } : {}, ...options.v ? { v: this.turn(options.v) } : {} };
|
|
7192
7285
|
}
|
|
7193
|
-
spawn(system,
|
|
7194
|
-
const options = this.placed(
|
|
7286
|
+
spawn(system, recipe2) {
|
|
7287
|
+
const options = this.placed(recipe2);
|
|
7195
7288
|
this.context.trace?.(system, [options]);
|
|
7196
7289
|
if (system === "add") this.add.spawn(options);
|
|
7197
7290
|
else if (system === "normal") this.normal.spawn(options);
|
|
@@ -7211,43 +7304,43 @@ var GameDestructionThree = (() => {
|
|
|
7211
7304
|
this.accumulators.set(key, carried);
|
|
7212
7305
|
}
|
|
7213
7306
|
/** A solid particle (a cone, a sphere): the group sits at `p` turned by the placement, `v` is turned with it and `wv` is not. */
|
|
7214
|
-
mesh(
|
|
7307
|
+
mesh(recipe2) {
|
|
7215
7308
|
const placement = this.placement;
|
|
7216
|
-
const options = placement ? { ...
|
|
7309
|
+
const options = placement ? { ...recipe2, p: this.place(recipe2.p), ...recipe2.v ? { v: this.turn(recipe2.v) } : {} } : recipe2;
|
|
7217
7310
|
this.context.trace?.("mesh", [options]);
|
|
7218
|
-
const grouped =
|
|
7311
|
+
const grouped = recipe2.at !== void 0 || recipe2.roll !== void 0;
|
|
7219
7312
|
return this.meshes.spawn(options, placement && grouped ? placement.orientation ?? [0, Math.sin(placement.yaw / 2), 0, Math.cos(placement.yaw / 2)] : void 0);
|
|
7220
7313
|
}
|
|
7221
|
-
debrisPiece(
|
|
7222
|
-
const options = this.placed(
|
|
7314
|
+
debrisPiece(recipe2) {
|
|
7315
|
+
const options = this.placed(recipe2);
|
|
7223
7316
|
this.context.trace?.("debris", [options]);
|
|
7224
7317
|
this.debris.spawn(options);
|
|
7225
7318
|
}
|
|
7226
|
-
decal(
|
|
7227
|
-
let args =
|
|
7319
|
+
decal(recipe2) {
|
|
7320
|
+
let args = recipe2;
|
|
7228
7321
|
if (this.placement) {
|
|
7229
|
-
const [x, y, z] = this.place([
|
|
7230
|
-
const options =
|
|
7231
|
-
args = [x, y, z,
|
|
7322
|
+
const [x, y, z] = this.place([recipe2[0], recipe2[1], recipe2[2]]);
|
|
7323
|
+
const options = recipe2[5];
|
|
7324
|
+
args = [x, y, z, recipe2[3], recipe2[4], options?.n ? { ...options, n: this.turn(options.n) } : options];
|
|
7232
7325
|
}
|
|
7233
7326
|
this.context.trace?.("decal", args);
|
|
7234
7327
|
this.decals.add(args[0], args[1], args[2], args[3], args[4], args[5]);
|
|
7235
7328
|
}
|
|
7236
|
-
ring(
|
|
7237
|
-
const args = this.placement ? [this.place(
|
|
7329
|
+
ring(recipe2) {
|
|
7330
|
+
const args = this.placement ? [this.place(recipe2[0]), recipe2[1], recipe2[2], recipe2[3], recipe2[4], recipe2[5] && this.turn(recipe2[5]), recipe2[6]] : recipe2;
|
|
7238
7331
|
this.context.trace?.("ring", args);
|
|
7239
7332
|
this.rings.add(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
|
|
7240
7333
|
}
|
|
7241
7334
|
/** `steady` (a seventh number): the light holds its intensity for its duration and goes out, where the Workshop's fades with the square of its age. */
|
|
7242
|
-
flash(
|
|
7243
|
-
const args = this.placement ? [this.place(
|
|
7335
|
+
flash(recipe2) {
|
|
7336
|
+
const args = this.placement ? [this.place(recipe2[0]), recipe2[1], recipe2[2], recipe2[3], recipe2[4], recipe2[5], recipe2[6]] : recipe2;
|
|
7244
7337
|
this.context.trace?.("flash", args.slice(0, 6));
|
|
7245
7338
|
this.lights.flash(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
|
|
7246
7339
|
}
|
|
7247
7340
|
/** `fireLight` (W400): sways with the clock and flickers with one draw. */
|
|
7248
|
-
fireLight(slot,
|
|
7341
|
+
fireLight(slot, recipe2, intensity, time) {
|
|
7249
7342
|
const light = this.lights.fire[slot];
|
|
7250
|
-
const point = this.place(
|
|
7343
|
+
const point = this.place(recipe2);
|
|
7251
7344
|
light.x = point[0];
|
|
7252
7345
|
light.y = point[1];
|
|
7253
7346
|
light.z = point[2];
|
|
@@ -7561,7 +7654,22 @@ var GameDestructionThree = (() => {
|
|
|
7561
7654
|
return key === void 0 ? void 0 : entry[key];
|
|
7562
7655
|
}
|
|
7563
7656
|
|
|
7564
|
-
// ../game-
|
|
7657
|
+
// ../game-render-three/dist/index.js
|
|
7658
|
+
var DEFAULT_ANGLE = Math.PI / 3;
|
|
7659
|
+
var LIGHT_PRIORITY = {
|
|
7660
|
+
blast: 900,
|
|
7661
|
+
rocket: 800,
|
|
7662
|
+
muzzle: 750,
|
|
7663
|
+
fire: 600,
|
|
7664
|
+
flare: 500,
|
|
7665
|
+
headlight: 400,
|
|
7666
|
+
weaponLight: 400,
|
|
7667
|
+
lamp: 200,
|
|
7668
|
+
street: 150
|
|
7669
|
+
};
|
|
7670
|
+
var EFFECT_STEP_SECONDS = 1 / 50;
|
|
7671
|
+
|
|
7672
|
+
// ../game-vfx-three/dist/chunk-ALDCQ5RH.js
|
|
7565
7673
|
var GLOW_VERTEX = `attribute vec4 aP;attribute vec4 aC;varying vec2 vU;varying vec4 vC;
|
|
7566
7674
|
void main(){vec3 R=vec3(viewMatrix[0][0],viewMatrix[1][0],viewMatrix[2][0]);vec3 Up=vec3(viewMatrix[0][1],viewMatrix[1][1],viewMatrix[2][1]);
|
|
7567
7675
|
vec3 wp=aP.xyz+(R*position.x+Up*position.y)*aP.w;vU=position.xy;vC=aC;gl_Position=projectionMatrix*viewMatrix*vec4(wp,1.);}`;
|
|
@@ -7840,8 +7948,26 @@ void main(){
|
|
|
7840
7948
|
}
|
|
7841
7949
|
};
|
|
7842
7950
|
}
|
|
7843
|
-
|
|
7951
|
+
var LIGHT_ON_THRESHOLD = 0.05;
|
|
7952
|
+
var FIRE_COLOR = 16742954;
|
|
7953
|
+
var FIRE_DECAY = 2;
|
|
7954
|
+
var BLAST_DECAY = 2;
|
|
7955
|
+
function followEntry(budget, handles, index, entry, request) {
|
|
7956
|
+
const handle = handles[index] ?? null;
|
|
7957
|
+
if (entry.intensity <= LIGHT_ON_THRESHOLD) {
|
|
7958
|
+
handle?.release({ fadeMs: 0 });
|
|
7959
|
+
handles[index] = null;
|
|
7960
|
+
return;
|
|
7961
|
+
}
|
|
7962
|
+
if (handle?.alive) {
|
|
7963
|
+
handle.update({ color: request.color, intensity: request.intensity, distance: request.distance, position: request.position });
|
|
7964
|
+
return;
|
|
7965
|
+
}
|
|
7966
|
+
handles[index] = budget.acquire(request);
|
|
7967
|
+
}
|
|
7968
|
+
function createLightLayer(THREE, pool, scale, budget) {
|
|
7844
7969
|
const group = new THREE.Group();
|
|
7970
|
+
if (budget) return createBudgetedLightLayer(group, pool, scale, budget);
|
|
7845
7971
|
const blast = pool.blast.map(() => {
|
|
7846
7972
|
const light = new THREE.PointLight(16752720, 0, 30, 2);
|
|
7847
7973
|
light.position.set(0, -50, 0);
|
|
@@ -7880,6 +8006,30 @@ void main(){
|
|
|
7880
8006
|
}
|
|
7881
8007
|
};
|
|
7882
8008
|
}
|
|
8009
|
+
function createBudgetedLightLayer(group, pool, scale, budget) {
|
|
8010
|
+
const blast = pool.blast.map(() => null);
|
|
8011
|
+
const fire = pool.fire.map(() => null);
|
|
8012
|
+
return {
|
|
8013
|
+
group,
|
|
8014
|
+
sync() {
|
|
8015
|
+
let lit = 0;
|
|
8016
|
+
pool.blast.forEach((entry, index) => {
|
|
8017
|
+
const request = { owner: "vfx.blast", priority: LIGHT_PRIORITY.blast, urgent: true, color: entry.color, intensity: entry.intensity * scale, distance: entry.distance, decay: BLAST_DECAY, position: [entry.x, entry.y, entry.z] };
|
|
8018
|
+
followEntry(budget, blast, index, entry, request);
|
|
8019
|
+
if (entry.intensity > LIGHT_ON_THRESHOLD) lit++;
|
|
8020
|
+
});
|
|
8021
|
+
pool.fire.forEach((entry, index) => {
|
|
8022
|
+
const request = { owner: "vfx.fire", priority: LIGHT_PRIORITY.fire, color: FIRE_COLOR, intensity: entry.intensity * scale, distance: entry.distance, decay: FIRE_DECAY, position: [entry.x, entry.y, entry.z] };
|
|
8023
|
+
followEntry(budget, fire, index, entry, request);
|
|
8024
|
+
if (entry.intensity > LIGHT_ON_THRESHOLD) lit++;
|
|
8025
|
+
});
|
|
8026
|
+
return lit;
|
|
8027
|
+
},
|
|
8028
|
+
dispose() {
|
|
8029
|
+
for (const handle of [...blast, ...fire]) handle?.release({ fadeMs: 0 });
|
|
8030
|
+
}
|
|
8031
|
+
};
|
|
8032
|
+
}
|
|
7883
8033
|
var SCORCH_SEED = 5;
|
|
7884
8034
|
var TEXTURE_SIZE = 256;
|
|
7885
8035
|
var FULL_TURN = Math.PI * 2;
|
|
@@ -8013,6 +8163,7 @@ void main(){
|
|
|
8013
8163
|
group.add(mesh2);
|
|
8014
8164
|
return mesh2;
|
|
8015
8165
|
}
|
|
8166
|
+
let probeGeometry;
|
|
8016
8167
|
const groundMarks = [];
|
|
8017
8168
|
const spareGeometries = [];
|
|
8018
8169
|
const blood = [];
|
|
@@ -8118,6 +8269,15 @@ void main(){
|
|
|
8118
8269
|
rockMesh();
|
|
8119
8270
|
plateMesh();
|
|
8120
8271
|
},
|
|
8272
|
+
probes() {
|
|
8273
|
+
probeGeometry ?? (probeGeometry = new THREE.PlaneGeometry(1e-3, 1e-3).rotateX(-Math.PI / 2));
|
|
8274
|
+
return [...scorchMaterials, dirtMaterial].map((material) => {
|
|
8275
|
+
const mesh2 = new THREE.Mesh(probeGeometry, material);
|
|
8276
|
+
mesh2.receiveShadow = true;
|
|
8277
|
+
mesh2.frustumCulled = false;
|
|
8278
|
+
return mesh2;
|
|
8279
|
+
});
|
|
8280
|
+
},
|
|
8121
8281
|
update(dt) {
|
|
8122
8282
|
for (let index = 0; index < blood.length; index++) {
|
|
8123
8283
|
const drawn = blood[index];
|
|
@@ -8130,6 +8290,7 @@ void main(){
|
|
|
8130
8290
|
dispose() {
|
|
8131
8291
|
clearMarks();
|
|
8132
8292
|
for (const mesh2 of [instanced.blood, instanced.rock, instanced.plate]) mesh2?.dispose();
|
|
8293
|
+
probeGeometry?.dispose();
|
|
8133
8294
|
for (const item of owned) item.dispose();
|
|
8134
8295
|
}
|
|
8135
8296
|
};
|
|
@@ -8705,7 +8866,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8705
8866
|
}
|
|
8706
8867
|
};
|
|
8707
8868
|
}
|
|
8708
|
-
function createWorkshopLayers(THREE, engine, pointScale, lightScale) {
|
|
8869
|
+
function createWorkshopLayers(THREE, engine, pointScale, lightScale, budget) {
|
|
8709
8870
|
const group = new THREE.Group();
|
|
8710
8871
|
group.name = "workshop";
|
|
8711
8872
|
const layers = [
|
|
@@ -8720,15 +8881,30 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8720
8881
|
for (const layer2 of layers) group.add(layer2.object);
|
|
8721
8882
|
const meshes = createMeshLayer(THREE, engine.meshes);
|
|
8722
8883
|
group.add(meshes.group);
|
|
8723
|
-
const
|
|
8724
|
-
const fireLights = engine.lights.fire.map(() => new THREE.PointLight(FIRE_LIGHT_COLOR, 0, FIRE_LIGHT_DISTANCE));
|
|
8725
|
-
for (const light of [...flashLights, ...fireLights]) group.add(light);
|
|
8884
|
+
const lights = budget ? budgetedLights(engine, lightScale, budget) : legacyLights(THREE, engine, lightScale, group);
|
|
8726
8885
|
return {
|
|
8727
8886
|
group,
|
|
8728
8887
|
sync() {
|
|
8729
8888
|
let drawCalls = 0;
|
|
8730
8889
|
for (const layer2 of layers) if (layer2.sync()) drawCalls++;
|
|
8731
8890
|
drawCalls += meshes.sync();
|
|
8891
|
+
lights.sync();
|
|
8892
|
+
return drawCalls;
|
|
8893
|
+
},
|
|
8894
|
+
dispose() {
|
|
8895
|
+
for (const layer2 of layers) layer2.dispose();
|
|
8896
|
+
meshes.dispose();
|
|
8897
|
+
lights.dispose();
|
|
8898
|
+
group.clear();
|
|
8899
|
+
}
|
|
8900
|
+
};
|
|
8901
|
+
}
|
|
8902
|
+
function legacyLights(THREE, engine, lightScale, group) {
|
|
8903
|
+
const flashLights = engine.lights.lights.map(() => new THREE.PointLight(16760944, 0, 14));
|
|
8904
|
+
const fireLights = engine.lights.fire.map(() => new THREE.PointLight(FIRE_LIGHT_COLOR, 0, FIRE_LIGHT_DISTANCE));
|
|
8905
|
+
for (const light of [...flashLights, ...fireLights]) group.add(light);
|
|
8906
|
+
return {
|
|
8907
|
+
sync() {
|
|
8732
8908
|
engine.lights.lights.forEach((source, index) => {
|
|
8733
8909
|
const light = flashLights[index];
|
|
8734
8910
|
light.position.set(source.x, source.y, source.z);
|
|
@@ -8741,12 +8917,27 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8741
8917
|
light.position.set(source.x, source.y, source.z);
|
|
8742
8918
|
light.intensity = source.intensity * lightScale;
|
|
8743
8919
|
});
|
|
8744
|
-
return drawCalls;
|
|
8745
8920
|
},
|
|
8746
8921
|
dispose() {
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8922
|
+
}
|
|
8923
|
+
};
|
|
8924
|
+
}
|
|
8925
|
+
function budgetedLights(engine, lightScale, budget) {
|
|
8926
|
+
const flash2 = engine.lights.lights.map(() => null);
|
|
8927
|
+
const fire = engine.lights.fire.map(() => null);
|
|
8928
|
+
return {
|
|
8929
|
+
sync() {
|
|
8930
|
+
engine.lights.lights.forEach((source, index) => {
|
|
8931
|
+
const request = { owner: "vfx.workshop", priority: LIGHT_PRIORITY.blast, urgent: true, color: source.color, intensity: source.intensity * lightScale, distance: source.distance, position: [source.x, source.y, source.z] };
|
|
8932
|
+
followEntry(budget, flash2, index, source, request);
|
|
8933
|
+
});
|
|
8934
|
+
engine.lights.fire.forEach((source, index) => {
|
|
8935
|
+
const request = { owner: "vfx.workshop", priority: LIGHT_PRIORITY.fire, color: FIRE_LIGHT_COLOR, intensity: source.intensity * lightScale, distance: FIRE_LIGHT_DISTANCE, position: [source.x, source.y, source.z] };
|
|
8936
|
+
followEntry(budget, fire, index, source, request);
|
|
8937
|
+
});
|
|
8938
|
+
},
|
|
8939
|
+
dispose() {
|
|
8940
|
+
for (const handle of [...flash2, ...fire]) handle?.release({ fadeMs: 0 });
|
|
8750
8941
|
}
|
|
8751
8942
|
};
|
|
8752
8943
|
}
|
|
@@ -8804,7 +8995,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8804
8995
|
const chunks = createChunkLayer(THREE, pools.chunks, { rock: rockGeometry, plate: plateGeometry }, colorOfChunk);
|
|
8805
8996
|
const shocks = createShockLayer(THREE, pools.shocks);
|
|
8806
8997
|
const sprites = createSpriteLayer(THREE, world.sprites(), options.pointScale ?? 700);
|
|
8807
|
-
const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI);
|
|
8998
|
+
const lights = createLightLayer(THREE, pools.lights, options.lightScale ?? Math.PI, options.lights);
|
|
8808
8999
|
const marks = createMarkLayer(THREE, { ground: pools.ground, createCanvas: options.createCanvas, rockGeometry, plateGeometry, ...options.markLimits });
|
|
8809
9000
|
root.add(puffs.mesh, sparks.mesh, glows.mesh, chunks.rocks, chunks.plates, shocks.group, lights.group, sprites.additive, sprites.normal, marks.group);
|
|
8810
9001
|
const stopMarks = world.onMarks((event) => marks.handle(event));
|
|
@@ -8837,7 +9028,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8837
9028
|
workshopLayers?.dispose();
|
|
8838
9029
|
if (workshopLayers) root.remove(workshopLayers.group);
|
|
8839
9030
|
workshopEngine = engine;
|
|
8840
|
-
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1);
|
|
9031
|
+
workshopLayers = createWorkshopLayers(THREE, engine, options.pointScale ?? 700, 1, options.lights);
|
|
8841
9032
|
root.add(workshopLayers.group);
|
|
8842
9033
|
applyLayers();
|
|
8843
9034
|
}
|
|
@@ -8910,7 +9101,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
8910
9101
|
const meshes = createMeshLayerProbes(THREE);
|
|
8911
9102
|
for (const mesh2 of meshes) mesh2.frustumCulled = false;
|
|
8912
9103
|
const group = new THREE.Group();
|
|
8913
|
-
group.add(...meshes);
|
|
9104
|
+
group.add(...meshes, ...marks.probes());
|
|
8914
9105
|
anchors = { meshes, group };
|
|
8915
9106
|
return { group, dispose: () => {
|
|
8916
9107
|
} };
|
|
@@ -9028,6 +9219,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9028
9219
|
var BIG_LENGTH = 0.2;
|
|
9029
9220
|
var BIG_WIDTH = 0.03;
|
|
9030
9221
|
var FRAGMENT_SEED = 1374496523;
|
|
9222
|
+
var SHARD_SHAPES = 6;
|
|
9031
9223
|
var isBig = (size) => {
|
|
9032
9224
|
const [, middle, longest] = [...size].sort((a, b) => a - b);
|
|
9033
9225
|
return longest >= BIG_LENGTH && middle >= BIG_WIDTH;
|
|
@@ -9077,7 +9269,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9077
9269
|
return m;
|
|
9078
9270
|
}
|
|
9079
9271
|
shardGeometry(seed) {
|
|
9080
|
-
const idx = seed %
|
|
9272
|
+
const idx = seed % SHARD_SHAPES;
|
|
9081
9273
|
let g = this.shardGeos[idx];
|
|
9082
9274
|
if (!g) {
|
|
9083
9275
|
const r = createRng(1e3 + idx);
|
|
@@ -9098,17 +9290,32 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9098
9290
|
}
|
|
9099
9291
|
return g;
|
|
9100
9292
|
}
|
|
9293
|
+
markParts() {
|
|
9294
|
+
this.markGeometry ?? (this.markGeometry = new this.THREE.CircleGeometry(1, 10));
|
|
9295
|
+
this.markMaterial ?? (this.markMaterial = new this.THREE.MeshBasicMaterial({ color: 723465, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 }));
|
|
9296
|
+
return { geometry: this.markGeometry, material: this.markMaterial };
|
|
9297
|
+
}
|
|
9298
|
+
/**
|
|
9299
|
+
* Meshes, never put in a scene by this class, that draw every shared piece of a fragment once: the box, the six shards, the bullet-hole disc, and the opaque and
|
|
9300
|
+
* glass materials. A host draws them at load so the first blast does not upload these buffers in the middle of a frame.
|
|
9301
|
+
*/
|
|
9302
|
+
warmMeshes() {
|
|
9303
|
+
const opaque = this.materialFor("warm", 8421504, false);
|
|
9304
|
+
const glass = this.materialFor("warm", 8421504, true);
|
|
9305
|
+
const geometries = [this.box, ...Array.from({ length: SHARD_SHAPES }, (_, index) => this.shardGeometry(index))];
|
|
9306
|
+
const { geometry, material } = this.markParts();
|
|
9307
|
+
return [...geometries.flatMap((shared) => [new this.THREE.Mesh(shared, opaque), new this.THREE.Mesh(shared, glass)]), new this.THREE.Mesh(geometry, material)];
|
|
9308
|
+
}
|
|
9101
9309
|
/**
|
|
9102
9310
|
* Bullet holes that stay on a fragment (`Fragment.marks`): dark discs that are children of its mesh, so they fall, spin and settle with it. The mesh is
|
|
9103
9311
|
* a unit box scaled to the fragment, so each disc is scaled back; exact for a mark on a face, which is where a bullet hole is.
|
|
9104
9312
|
*/
|
|
9105
9313
|
addMarks(mesh2, f) {
|
|
9106
9314
|
if (!f.marks?.length) return;
|
|
9107
|
-
|
|
9108
|
-
this.markMaterial ?? (this.markMaterial = new this.THREE.MeshBasicMaterial({ color: 723465, depthWrite: false, polygonOffset: true, polygonOffsetFactor: -4, polygonOffsetUnits: -4 }));
|
|
9315
|
+
const { geometry, material } = this.markParts();
|
|
9109
9316
|
const sc = mesh2.scale;
|
|
9110
9317
|
for (const mark of f.marks) {
|
|
9111
|
-
const disc = new this.THREE.Mesh(
|
|
9318
|
+
const disc = new this.THREE.Mesh(geometry, material);
|
|
9112
9319
|
const radius = Math.max(4e-3, mark.diameter) * 0.6;
|
|
9113
9320
|
const n = this.v.set(mark.normal[0], mark.normal[1], mark.normal[2]).normalize();
|
|
9114
9321
|
disc.position.set(mark.point[0] / sc.x + n.x * 2e-3 / sc.x, mark.point[1] / sc.y + n.y * 2e-3 / sc.y, mark.point[2] / sc.z + n.z * 2e-3 / sc.z);
|
|
@@ -9691,7 +9898,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9691
9898
|
const invM = 1 / b.mass;
|
|
9692
9899
|
const steps = clamp6(Math.ceil(dt / SUB_DT), 1, 8);
|
|
9693
9900
|
const h = dt / steps;
|
|
9694
|
-
let
|
|
9901
|
+
let landing2 = 0;
|
|
9695
9902
|
let landPoint = [pos[0], pos[1], pos[2]];
|
|
9696
9903
|
let contactedLast = false;
|
|
9697
9904
|
let touched = false;
|
|
@@ -9797,8 +10004,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9797
10004
|
let impact2 = 0;
|
|
9798
10005
|
for (const r of rs) impact2 = Math.max(impact2, -(v[1] + w[2] * r[0] - w[0] * r[2]));
|
|
9799
10006
|
const restitution = impact2 > 2 ? 0.25 : 0;
|
|
9800
|
-
if (impact2 >
|
|
9801
|
-
|
|
10007
|
+
if (impact2 > landing2) {
|
|
10008
|
+
landing2 = impact2;
|
|
9802
10009
|
let sx = 0;
|
|
9803
10010
|
let sz = 0;
|
|
9804
10011
|
for (const r of rs) {
|
|
@@ -9934,9 +10141,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9934
10141
|
b.root.position.set(pos[0] - off.x, pos[1] - off.y, pos[2] - off.z);
|
|
9935
10142
|
b.root.updateMatrixWorld(true);
|
|
9936
10143
|
b.onGround = contactedLast;
|
|
9937
|
-
if (
|
|
10144
|
+
if (landing2 > HARD_LANDING && this.time - b.lastHit > 0.15) {
|
|
9938
10145
|
b.lastHit = this.time;
|
|
9939
|
-
this.o.onHardLanding(b, landPoint,
|
|
10146
|
+
this.o.onHardLanding(b, landPoint, landing2);
|
|
9940
10147
|
}
|
|
9941
10148
|
const still = touched && !unstable && Math.hypot(v[0], v[1], v[2]) < 0.12 && Math.hypot(w[0], w[1], w[2]) < 0.45;
|
|
9942
10149
|
if (still) b.still += dt;
|
|
@@ -11416,6 +11623,69 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11416
11623
|
return out;
|
|
11417
11624
|
};
|
|
11418
11625
|
var RESTING_HOLD_COS = Math.cos(Math.PI / 6);
|
|
11626
|
+
var assemblies_default = [
|
|
11627
|
+
{
|
|
11628
|
+
id: "brick_wall_plastered",
|
|
11629
|
+
name: "Plastered brick wall",
|
|
11630
|
+
core: { material: "brick", thickness: 0.09 },
|
|
11631
|
+
layers: [{ material: "plaster", thickness: 0.02, faces: "both" }],
|
|
11632
|
+
units: { size: [0.23, 0.075, 0.11], bond: "running", mortar: "plaster" },
|
|
11633
|
+
breach: { area: 0.25 }
|
|
11634
|
+
},
|
|
11635
|
+
{
|
|
11636
|
+
id: "concrete_block_wall",
|
|
11637
|
+
name: "Concrete block wall",
|
|
11638
|
+
core: { material: "concrete", thickness: 0.14 },
|
|
11639
|
+
fill: 0.55,
|
|
11640
|
+
units: { size: [0.39, 0.19, 0.14], bond: "running", mortar: "plaster" },
|
|
11641
|
+
breach: { area: 0.25 }
|
|
11642
|
+
},
|
|
11643
|
+
{
|
|
11644
|
+
id: "reinforced_concrete",
|
|
11645
|
+
name: "Reinforced concrete wall",
|
|
11646
|
+
core: { material: "concrete", thickness: 0.2 },
|
|
11647
|
+
reinforcement: { material: "steel", spacing: 0.18, diameter: 8e-3, residualCapacity: 0.15 },
|
|
11648
|
+
units: { size: [0.6, 0.42, 0.2], bond: "stack", mortar: "concrete" },
|
|
11649
|
+
breach: { area: 0.25 }
|
|
11650
|
+
},
|
|
11651
|
+
{
|
|
11652
|
+
id: "drywall",
|
|
11653
|
+
name: "Drywall partition on steel studs",
|
|
11654
|
+
core: { material: "plaster", thickness: 0.0125 },
|
|
11655
|
+
layers: [{ material: "plaster", thickness: 0.0125, faces: "+" }],
|
|
11656
|
+
depth: 0.095,
|
|
11657
|
+
breach: { area: 0.25 }
|
|
11658
|
+
},
|
|
11659
|
+
{
|
|
11660
|
+
id: "wood_frame_wall",
|
|
11661
|
+
name: "Wood frame wall",
|
|
11662
|
+
core: { material: "plywood", thickness: 0.012 },
|
|
11663
|
+
layers: [{ material: "plaster", thickness: 0.0125, faces: "-" }],
|
|
11664
|
+
depth: 0.1135,
|
|
11665
|
+
breach: { area: 0.25 }
|
|
11666
|
+
},
|
|
11667
|
+
{
|
|
11668
|
+
id: "log_wall",
|
|
11669
|
+
name: "Log wall",
|
|
11670
|
+
core: { material: "wood_soft", thickness: 0.2 },
|
|
11671
|
+
fill: 0.785,
|
|
11672
|
+
breach: { area: 0.25 }
|
|
11673
|
+
},
|
|
11674
|
+
{
|
|
11675
|
+
id: "sheet_metal_shack",
|
|
11676
|
+
name: "Corrugated sheet metal wall",
|
|
11677
|
+
core: { material: "steel", thickness: 6e-4 },
|
|
11678
|
+
depth: 0.02,
|
|
11679
|
+
breach: { area: 0.25 }
|
|
11680
|
+
},
|
|
11681
|
+
{
|
|
11682
|
+
id: "glass_pane",
|
|
11683
|
+
name: "Glass pane",
|
|
11684
|
+
core: { material: "glass", thickness: 6e-3 },
|
|
11685
|
+
breach: { area: 0.1 }
|
|
11686
|
+
}
|
|
11687
|
+
];
|
|
11688
|
+
var SHIPPED = structuredClone(assemblies_default);
|
|
11419
11689
|
|
|
11420
11690
|
// src/spec.ts
|
|
11421
11691
|
function containsOf(value) {
|
|
@@ -11781,6 +12051,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11781
12051
|
this.b = tmpV(THREE);
|
|
11782
12052
|
this.m4 = new THREE.Matrix4();
|
|
11783
12053
|
this.ray = new THREE.Raycaster();
|
|
12054
|
+
this.ray.layers.enableAll();
|
|
11784
12055
|
this.box = new THREE.Box3();
|
|
11785
12056
|
this.t = tmpV(THREE);
|
|
11786
12057
|
this.lbox = new THREE.Box3();
|
|
@@ -11788,9 +12059,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11788
12059
|
/**
|
|
11789
12060
|
* @param range vertex range `[v0, v1)` of a merged mesh this part is drawn by (see `MeshRange`); `hosts` caches the shared state per mesh for one `bind`.
|
|
11790
12061
|
*/
|
|
11791
|
-
makeMeshVis(mesh2, home,
|
|
12062
|
+
makeMeshVis(mesh2, home, range2, hosts) {
|
|
11792
12063
|
let host = null;
|
|
11793
|
-
if (
|
|
12064
|
+
if (range2) {
|
|
11794
12065
|
host = hosts?.get(mesh2) ?? null;
|
|
11795
12066
|
if (!host) {
|
|
11796
12067
|
host = { mesh: mesh2, origGeometry: mesh2.geometry, origMaterial: mesh2.material, ownGeo: false, work: null, ownMat: false, sharers: [] };
|
|
@@ -11816,8 +12087,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
11816
12087
|
bottomY: 0,
|
|
11817
12088
|
moved: false,
|
|
11818
12089
|
host,
|
|
11819
|
-
range:
|
|
11820
|
-
merged: host &&
|
|
12090
|
+
range: range2 ? [range2[0], range2[1]] : null,
|
|
12091
|
+
merged: host && range2 ? { host, range: [range2[0], range2[1]] } : null,
|
|
11821
12092
|
loose: false,
|
|
11822
12093
|
collapsed: false,
|
|
11823
12094
|
hidden: false,
|
|
@@ -12386,24 +12657,24 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12386
12657
|
geo.boundingBox = null;
|
|
12387
12658
|
geo.boundingSphere = null;
|
|
12388
12659
|
}
|
|
12389
|
-
collapseRange(arr,
|
|
12390
|
-
const x = arr[
|
|
12391
|
-
const y = arr[
|
|
12392
|
-
const z = arr[
|
|
12393
|
-
for (let i =
|
|
12660
|
+
collapseRange(arr, range2) {
|
|
12661
|
+
const x = arr[range2[0] * 3];
|
|
12662
|
+
const y = arr[range2[0] * 3 + 1];
|
|
12663
|
+
const z = arr[range2[0] * 3 + 2];
|
|
12664
|
+
for (let i = range2[0] * 3; i < range2[1] * 3; i += 3) {
|
|
12394
12665
|
arr[i] = x;
|
|
12395
12666
|
arr[i + 1] = y;
|
|
12396
12667
|
arr[i + 2] = z;
|
|
12397
12668
|
}
|
|
12398
12669
|
}
|
|
12399
12670
|
/** Melt of one range: shorter in Y (down to 25 %) with the bottom fixed, a bit wider around its centre, with the same noise as a whole mesh. */
|
|
12400
|
-
meltRange(arr, work,
|
|
12671
|
+
meltRange(arr, work, range2, melt) {
|
|
12401
12672
|
let minY = Infinity;
|
|
12402
12673
|
let maxY = -Infinity;
|
|
12403
12674
|
let cx = 0;
|
|
12404
12675
|
let cz = 0;
|
|
12405
|
-
const n = Math.max(1,
|
|
12406
|
-
for (let i =
|
|
12676
|
+
const n = Math.max(1, range2[1] - range2[0]);
|
|
12677
|
+
for (let i = range2[0] * 3; i < range2[1] * 3; i += 3) {
|
|
12407
12678
|
minY = Math.min(minY, work[i + 1]);
|
|
12408
12679
|
maxY = Math.max(maxY, work[i + 1]);
|
|
12409
12680
|
cx += work[i];
|
|
@@ -12414,7 +12685,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12414
12685
|
const h = Math.max(1e-4, maxY - minY);
|
|
12415
12686
|
const sy = lerp5(1, 0.25, melt);
|
|
12416
12687
|
const sxz = 1 + 0.18 * melt;
|
|
12417
|
-
for (let i =
|
|
12688
|
+
for (let i = range2[0] * 3, v = 0; i < range2[1] * 3; i += 3, v++) {
|
|
12418
12689
|
const noise = (Math.imul(v + 1, 2654435761) >>> 0) % 1e3 / 1e3;
|
|
12419
12690
|
arr[i] = cx + (work[i] - cx) * sxz;
|
|
12420
12691
|
arr[i + 2] = cz + (work[i + 2] - cz) * sxz;
|
|
@@ -12771,8 +13042,26 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12771
13042
|
__publicField(this, "queue", []);
|
|
12772
13043
|
/** Presentation work an event left for later frames (fragments still to spawn, loose groups still to split), run in order within the frame budget. */
|
|
12773
13044
|
__publicField(this, "slices", []);
|
|
12774
|
-
/** When the work of this frame must stop; `Infinity` outside
|
|
13045
|
+
/** When the work of this frame must stop; `Infinity` outside a drain, where a call finishes what it started. */
|
|
12775
13046
|
__publicField(this, "sliceDeadline", Infinity);
|
|
13047
|
+
/** The clock the drain reads; a budget lends its own while it runs the queue. */
|
|
13048
|
+
__publicField(this, "drainClock", () => performance.now());
|
|
13049
|
+
/** true once a frame budget owns the draining: `update` no longer drains by itself. */
|
|
13050
|
+
__publicField(this, "attached", false);
|
|
13051
|
+
__publicField(this, "eventQueue", {
|
|
13052
|
+
id: DEFERRED_QUEUE_ID.DESTRUCTION_EVENTS,
|
|
13053
|
+
pending: () => this.queue.length + this.slices.length,
|
|
13054
|
+
run: (deadline, clock) => this.runBudgeted(deadline, clock),
|
|
13055
|
+
flush: () => {
|
|
13056
|
+
for (let guard = 0; guard < 16 && this.queue.length + this.slices.length > 0; guard++) this.runBudgeted(Infinity, () => 0);
|
|
13057
|
+
},
|
|
13058
|
+
attach: () => {
|
|
13059
|
+
this.attached = true;
|
|
13060
|
+
},
|
|
13061
|
+
detach: () => {
|
|
13062
|
+
this.attached = false;
|
|
13063
|
+
}
|
|
13064
|
+
});
|
|
12776
13065
|
__publicField(this, "unsubscribe");
|
|
12777
13066
|
__publicField(this, "visuals");
|
|
12778
13067
|
__publicField(this, "fragments");
|
|
@@ -12818,6 +13107,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
12818
13107
|
const maxParticles = Math.max(8, Math.floor((o.maxParticles ?? 2500) * (this.low ? 0.5 : 1)));
|
|
12819
13108
|
this.visuals = new Visuals(THREE, this.low, 600);
|
|
12820
13109
|
this.ground = new GroundSpots(THREE, o.scene, this.groundAt, 48);
|
|
13110
|
+
if (!o.vfx) warnOnce("game-destruction-three.own-effects-world", "game-destruction-three made an effects world of its own because the host lent none (options.vfx); pass the host effects so the effects share one budget");
|
|
12821
13111
|
this.vfx = o.vfx ?? createVfx({ profile: this.quality, gravity: this.gravity, groundAt: this.groundAt, sprites: { capacity: maxParticles, density: this.low ? 0.6 : 1 } });
|
|
12822
13112
|
this.ownVfx = o.vfx ? void 0 : createVfxRenderer(THREE, { scene: o.scene, camera: o.camera ?? new THREE.PerspectiveCamera(), vfx: this.vfx, pointScale: o.pointScale ?? 700 });
|
|
12823
13113
|
this.stopLanding = this.vfx.onLand(this.owner, (x, _y, z, color, tag) => {
|
|
@@ -13055,7 +13345,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13055
13345
|
if (this.disposed) return;
|
|
13056
13346
|
dt = clamp6(dt, 0, 0.1);
|
|
13057
13347
|
this.time += dt;
|
|
13058
|
-
this.drainQueue();
|
|
13348
|
+
if (!this.attached) this.drainQueue();
|
|
13059
13349
|
for (const b of this.bounds.values()) b.onFrame?.(dt);
|
|
13060
13350
|
for (const b of this.bounds.values()) if (b.soilNode && this.world.state(b.id)?.soil?.status === "yielding") this.onSettle(b.id);
|
|
13061
13351
|
this.updateActive(dt);
|
|
@@ -13066,36 +13356,50 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13066
13356
|
this.ownVfx?.update(dt);
|
|
13067
13357
|
this.syncTransforms();
|
|
13068
13358
|
}
|
|
13359
|
+
deferredQueue() {
|
|
13360
|
+
return this.eventQueue;
|
|
13361
|
+
}
|
|
13069
13362
|
drainQueue() {
|
|
13363
|
+
this.runBudgeted(performance.now() + this.eventBudgetMs, () => performance.now());
|
|
13364
|
+
}
|
|
13365
|
+
/** Applies the waiting events in order until the deadline, after the slices earlier events left; always at least one unit when something waits. Returns the units run. */
|
|
13366
|
+
runBudgeted(deadline, clock) {
|
|
13070
13367
|
const batch = this.queue.splice(0, this.queue.length);
|
|
13071
|
-
const
|
|
13368
|
+
const outerClock = this.drainClock;
|
|
13369
|
+
this.drainClock = clock;
|
|
13072
13370
|
this.sliceDeadline = deadline;
|
|
13371
|
+
let ran = 0;
|
|
13073
13372
|
try {
|
|
13074
|
-
this.runSlices(deadline);
|
|
13373
|
+
ran = this.runSlices(deadline);
|
|
13075
13374
|
for (let i = 0; i < batch.length; i++) {
|
|
13076
|
-
if (i > 0 &&
|
|
13375
|
+
if ((i > 0 || ran > 0 && this.attached) && clock() >= deadline) {
|
|
13077
13376
|
this.queue.unshift(...batch.slice(i));
|
|
13078
|
-
return;
|
|
13377
|
+
return ran;
|
|
13079
13378
|
}
|
|
13080
13379
|
try {
|
|
13081
13380
|
this.apply(batch[i]);
|
|
13082
13381
|
} catch (err) {
|
|
13083
13382
|
if (typeof console !== "undefined") console.error("game-destruction-three: failed to apply", batch[i].type, err);
|
|
13084
13383
|
}
|
|
13384
|
+
ran++;
|
|
13085
13385
|
}
|
|
13086
13386
|
} finally {
|
|
13087
13387
|
this.sliceDeadline = Infinity;
|
|
13388
|
+
this.drainClock = outerClock;
|
|
13088
13389
|
}
|
|
13390
|
+
return ran;
|
|
13089
13391
|
}
|
|
13090
13392
|
/** Runs the work left by earlier frames until the deadline; at least one piece per frame, so it always finishes. */
|
|
13091
13393
|
runSlices(deadline) {
|
|
13092
|
-
|
|
13394
|
+
let ran = 0;
|
|
13395
|
+
for (; this.slices.length > 0 && (ran === 0 || this.drainClock() < deadline); ran++) {
|
|
13093
13396
|
try {
|
|
13094
13397
|
this.slices.shift()();
|
|
13095
13398
|
} catch (err) {
|
|
13096
13399
|
if (typeof console !== "undefined") console.error("game-destruction-three: failed to run deferred work", err);
|
|
13097
13400
|
}
|
|
13098
13401
|
}
|
|
13402
|
+
return ran;
|
|
13099
13403
|
}
|
|
13100
13404
|
/**
|
|
13101
13405
|
* The ground under an object settled or tilted: its parts are drawn under a node with the soil's offset (`ObjectState.soil.offset`), so the host's
|
|
@@ -13653,7 +13957,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13653
13957
|
const maxAlive = this.low ? Math.floor(this.maxFragments / 2) : this.maxFragments;
|
|
13654
13958
|
const step = this.low ? 2 : 1;
|
|
13655
13959
|
for (let i = from; i < ev.fragments.length; i += step) {
|
|
13656
|
-
if (i - from >= MIN_FRAGMENTS_PER_SLICE * step &&
|
|
13960
|
+
if (i - from >= MIN_FRAGMENTS_PER_SLICE * step && this.drainClock() >= this.sliceDeadline) {
|
|
13657
13961
|
this.slices.push(() => this.spawnFragments(ev, i, look));
|
|
13658
13962
|
return;
|
|
13659
13963
|
}
|
|
@@ -13677,7 +13981,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
13677
13981
|
if (!st || st.groups.length < 2) return;
|
|
13678
13982
|
const loose = st.groups.length - 1;
|
|
13679
13983
|
for (let i = 0; i < loose; i++) {
|
|
13680
|
-
if (i > 0 &&
|
|
13984
|
+
if (i > 0 && this.drainClock() >= this.sliceDeadline) {
|
|
13681
13985
|
this.slices.push(() => {
|
|
13682
13986
|
this.staticDirty.add(objectId);
|
|
13683
13987
|
this.splitLoose(objectId);
|
|
@@ -14056,6 +14360,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14056
14360
|
const materials = [kit.discMaterial().clone(), kit.patchMaterial(8421504).clone(), kit.lineMaterial(0).clone()];
|
|
14057
14361
|
const group = new T.Group();
|
|
14058
14362
|
group.add(new T.Mesh(kit.discGeometry(), materials[0]), new T.Mesh(kit.discGeometry(), materials[1]), new T.LineSegments(geometry, materials[2]));
|
|
14363
|
+
group.add(...this.fragments.warmMeshes());
|
|
14059
14364
|
for (const probe of group.children) probe.frustumCulled = false;
|
|
14060
14365
|
this.warmProbes = { group, geometry, materials };
|
|
14061
14366
|
return { group, dispose: () => {
|
|
@@ -14086,7 +14391,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14086
14391
|
}
|
|
14087
14392
|
};
|
|
14088
14393
|
|
|
14089
|
-
// ../game-audio/dist/
|
|
14394
|
+
// ../game-audio/dist/chunk-DZITVFMJ.js
|
|
14090
14395
|
var DISTANCE_CURVES = {
|
|
14091
14396
|
/** The game's `sfx`: `vol / (1 + d² × 0.004)`, cutoff `/ (1 + d × 0.01)`, silent below 0.01 (jogo L62091–62092, L62101). */
|
|
14092
14397
|
game_sfx: { model: "inverse_square", k: 4e-3, cutoffK: 0.01, minGain: 0.01 },
|
|
@@ -14101,31 +14406,6 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14101
14406
|
/** The game's animal calls: `max(0, 1 − d / 70) × 0.25`, silent at or below 0.005 (jogo L66627–66628). */
|
|
14102
14407
|
animals: { model: "linear", range: 70, scale: 0.25, minGain: 5e-3 }
|
|
14103
14408
|
};
|
|
14104
|
-
var mix_profiles_default = [
|
|
14105
|
-
{
|
|
14106
|
-
id: "game",
|
|
14107
|
-
master: 0.5,
|
|
14108
|
-
noise: { seconds: 1.5, create: "lazy" },
|
|
14109
|
-
sources: ["jogo:L62077-62081 master 0.5 to the output", "jogo:L62082-62088 noise 1.5 s made by the first sound that needs it"]
|
|
14110
|
-
},
|
|
14111
|
-
{
|
|
14112
|
-
id: "art_scene",
|
|
14113
|
-
master: 0.85,
|
|
14114
|
-
compressor: { threshold: -12, ratio: 5 },
|
|
14115
|
-
reverb: { seconds: 2.2, decay: 3.4, wet: 0.4 },
|
|
14116
|
-
noise: { seconds: 2, create: "eager" },
|
|
14117
|
-
sources: [
|
|
14118
|
-
"cena:L1308 compressor -12 dB, ratio 5",
|
|
14119
|
-
"cena:L1309 master 0.85",
|
|
14120
|
-
"cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
|
|
14121
|
-
"cena:L1311 wet 0.4",
|
|
14122
|
-
"cena:L1312 noise 2 s made with the chain"
|
|
14123
|
-
]
|
|
14124
|
-
}
|
|
14125
|
-
];
|
|
14126
|
-
var MIX_PROFILES = Object.fromEntries(
|
|
14127
|
-
mix_profiles_default.map((profile) => [profile.id, profile])
|
|
14128
|
-
);
|
|
14129
14409
|
var game_sfx_default = {
|
|
14130
14410
|
sources: ["jogo:L62089-62107"],
|
|
14131
14411
|
note: "The game's sfx table P: [lowpass cutoff Hz, peak gain, seconds]. Played as a noise burst with the game's distance curve (falloff game_sfx) and a 0.05 s tail. Team names in `team`.",
|
|
@@ -14170,6 +14450,78 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14170
14450
|
};
|
|
14171
14451
|
}
|
|
14172
14452
|
var GAME_SFX_BANK = { id: "game_sfx", sounds: [...GAME_SFX_CLASSES, GAME_SFX_FALLBACK].map((row) => noiseClassRecipe(row)) };
|
|
14453
|
+
var mix_profiles_default = [
|
|
14454
|
+
{
|
|
14455
|
+
id: "game",
|
|
14456
|
+
master: 0.5,
|
|
14457
|
+
noise: { seconds: 1.5, create: "lazy" },
|
|
14458
|
+
sources: ["jogo:L62077-62081 master 0.5 to the output", "jogo:L62082-62088 noise 1.5 s made by the first sound that needs it"]
|
|
14459
|
+
},
|
|
14460
|
+
{
|
|
14461
|
+
id: "art_scene",
|
|
14462
|
+
master: 0.85,
|
|
14463
|
+
compressor: { threshold: -12, ratio: 5 },
|
|
14464
|
+
reverb: { seconds: 2.2, decay: 3.4, wet: 0.4 },
|
|
14465
|
+
noise: { seconds: 2, create: "eager" },
|
|
14466
|
+
sources: [
|
|
14467
|
+
"cena:L1308 compressor -12 dB, ratio 5",
|
|
14468
|
+
"cena:L1309 master 0.85",
|
|
14469
|
+
"cena:L1310 convolver, impulse 2.2 s of (1 - i/len)^3.4",
|
|
14470
|
+
"cena:L1311 wet 0.4",
|
|
14471
|
+
"cena:L1312 noise 2 s made with the chain"
|
|
14472
|
+
]
|
|
14473
|
+
}
|
|
14474
|
+
];
|
|
14475
|
+
var MIX_PROFILES = Object.fromEntries(
|
|
14476
|
+
mix_profiles_default.map((profile) => [profile.id, profile])
|
|
14477
|
+
);
|
|
14478
|
+
var BENCH_BANK = {
|
|
14479
|
+
id: "audio_bench",
|
|
14480
|
+
sounds: [
|
|
14481
|
+
{
|
|
14482
|
+
id: "bench_ping",
|
|
14483
|
+
bus: "ui",
|
|
14484
|
+
pan: true,
|
|
14485
|
+
duration: 0.25,
|
|
14486
|
+
layers: [
|
|
14487
|
+
{
|
|
14488
|
+
gain: 0.5,
|
|
14489
|
+
wet: 0.3,
|
|
14490
|
+
source: { kind: "osc", wave: "sine", frequency: { from: 880 } },
|
|
14491
|
+
envelope: [
|
|
14492
|
+
[0, 1e-4, "set", "fixed"],
|
|
14493
|
+
[2e-3, 1, "exp"],
|
|
14494
|
+
["1d", 1e-4, "exp", "fixed"]
|
|
14495
|
+
]
|
|
14496
|
+
}
|
|
14497
|
+
],
|
|
14498
|
+
falloff: DISTANCE_CURVES.art_scene,
|
|
14499
|
+
sources: ["sdk: bench probe, a 880 Hz ping through the art scene output stage"]
|
|
14500
|
+
},
|
|
14501
|
+
{
|
|
14502
|
+
id: "bench_hiss",
|
|
14503
|
+
bus: "ui",
|
|
14504
|
+
pan: true,
|
|
14505
|
+
duration: [0.3, 0.9],
|
|
14506
|
+
layers: [
|
|
14507
|
+
{
|
|
14508
|
+
gain: 0.4,
|
|
14509
|
+
source: { kind: "noise", offset: "random", offsetScale: 1.5, loop: true },
|
|
14510
|
+
filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 3e3 }, q: 0.7 }] },
|
|
14511
|
+
envelope: [
|
|
14512
|
+
[0, 1e-4, "set", "fixed"],
|
|
14513
|
+
[2e-3, 1, "exp"],
|
|
14514
|
+
["1d", 1e-4, "exp", "fixed"]
|
|
14515
|
+
]
|
|
14516
|
+
}
|
|
14517
|
+
],
|
|
14518
|
+
falloff: DISTANCE_CURVES.art_scene,
|
|
14519
|
+
sources: ["sdk: bench probe, a highpass hiss of random length"]
|
|
14520
|
+
}
|
|
14521
|
+
]
|
|
14522
|
+
};
|
|
14523
|
+
|
|
14524
|
+
// ../game-audio/dist/index.js
|
|
14173
14525
|
var ATTACK = 2e-3;
|
|
14174
14526
|
function lengthOf(d, attack) {
|
|
14175
14527
|
if (typeof d === "number") return attack + d;
|
|
@@ -14207,6 +14559,31 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14207
14559
|
envelope: envelope(ATTACK)
|
|
14208
14560
|
};
|
|
14209
14561
|
}
|
|
14562
|
+
function sceneVoice(o) {
|
|
14563
|
+
return {
|
|
14564
|
+
duration: lengthOf(o.d, 0),
|
|
14565
|
+
gain: o.vol,
|
|
14566
|
+
wet: 0.45,
|
|
14567
|
+
source: {
|
|
14568
|
+
kind: "formant_voice",
|
|
14569
|
+
wave: o.wave ?? "sawtooth",
|
|
14570
|
+
frequency: { from: o.f0, to: o.f1, curve: "linear", over: "duration" },
|
|
14571
|
+
vibrato: { rate: [4, 10], depth: 0.08 },
|
|
14572
|
+
formants: [
|
|
14573
|
+
{ frequency: o.fA, q: 4 },
|
|
14574
|
+
{ frequency: o.fB, q: 6 }
|
|
14575
|
+
],
|
|
14576
|
+
breath: { frequency: o.fB, q: 1.1, gain: o.noise },
|
|
14577
|
+
mix: 3.2
|
|
14578
|
+
},
|
|
14579
|
+
envelope: [
|
|
14580
|
+
[0, 1e-4, "set", "fixed"],
|
|
14581
|
+
[0.1, 1, "exp"],
|
|
14582
|
+
["0.6d", 1, "set"],
|
|
14583
|
+
["1d", 1e-4, "exp", "fixed"]
|
|
14584
|
+
]
|
|
14585
|
+
};
|
|
14586
|
+
}
|
|
14210
14587
|
var sceneMany = (count, make) => Array.from({ length: count }, make);
|
|
14211
14588
|
function sceneRecipe(id, layers, line, options = {}) {
|
|
14212
14589
|
return {
|
|
@@ -14220,51 +14597,172 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
14220
14597
|
sources: [`cena:${line}`]
|
|
14221
14598
|
};
|
|
14222
14599
|
}
|
|
14223
|
-
var
|
|
14224
|
-
|
|
14225
|
-
|
|
14226
|
-
|
|
14227
|
-
|
|
14228
|
-
|
|
14229
|
-
|
|
14230
|
-
|
|
14231
|
-
|
|
14232
|
-
|
|
14233
|
-
|
|
14234
|
-
|
|
14235
|
-
|
|
14236
|
-
|
|
14237
|
-
|
|
14238
|
-
|
|
14239
|
-
|
|
14240
|
-
|
|
14241
|
-
|
|
14242
|
-
|
|
14243
|
-
|
|
14244
|
-
|
|
14245
|
-
|
|
14246
|
-
|
|
14247
|
-
|
|
14248
|
-
|
|
14249
|
-
|
|
14250
|
-
|
|
14251
|
-
|
|
14252
|
-
|
|
14253
|
-
|
|
14254
|
-
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
|
|
14258
|
-
|
|
14259
|
-
|
|
14260
|
-
|
|
14261
|
-
|
|
14262
|
-
|
|
14263
|
-
|
|
14264
|
-
|
|
14265
|
-
|
|
14266
|
-
|
|
14600
|
+
var TAKES = [
|
|
14601
|
+
{ f: 1, t: 1, v: 1 },
|
|
14602
|
+
{ f: 1.08, t: 0.9, v: 0.92 },
|
|
14603
|
+
{ f: 0.93, t: 1.12, v: 1.05 },
|
|
14604
|
+
{ f: 1.03, t: 1.05, v: 0.88 }
|
|
14605
|
+
];
|
|
14606
|
+
var FOOTSTEP_FALLOFF = { model: "inverse_square", k: 0.03, cutoffK: 0.03, minGain: 4e-3 };
|
|
14607
|
+
var BODY_FALLOFF = { model: "inverse_square", k: 0.012, cutoffK: 0.015, minGain: 4e-3 };
|
|
14608
|
+
var SURFACES = {
|
|
14609
|
+
concrete: { heel: { f: 95, d: 0.05, vol: 0.55 }, roll: { type: "bandpass", f: 2300, q: 1.1, d: 0.05, vol: 0.38 }, grains: { count: 2, f: 4200, q: 2, spread: 0.04, vol: 0.08 } },
|
|
14610
|
+
asphalt: { heel: { f: 90, d: 0.05, vol: 0.5 }, roll: { type: "bandpass", f: 1700, q: 0.9, d: 0.06, vol: 0.36 }, grains: { count: 4, f: 3600, q: 2.5, spread: 0.06, vol: 0.1 } },
|
|
14611
|
+
wood: { heel: { f: 130, d: 0.08, vol: 0.55 }, roll: { type: "lowpass", f: 1300, q: 0.8, d: 0.06, vol: 0.3 }, ring: [{ f: 210, d: 0.1, vol: 0.18 }, { f: 470, d: 0.06, vol: 0.06 }] },
|
|
14612
|
+
tile: { heel: { f: 110, d: 0.04, vol: 0.45 }, roll: { type: "bandpass", f: 3600, q: 2, d: 0.03, vol: 0.42 }, ring: [{ f: 1900, d: 0.03, vol: 0.05 }] },
|
|
14613
|
+
dirt: { heel: { f: 75, d: 0.06, vol: 0.45 }, roll: { type: "lowpass", f: 750, q: 0.7, d: 0.09, vol: 0.4 }, grains: { count: 3, f: 1800, q: 1.5, spread: 0.07, vol: 0.06 } },
|
|
14614
|
+
grass: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "highpass", f: 2600, q: 0.7, d: 0.13, vol: 0.28 }, grains: { count: 4, f: 5200, q: 1.2, spread: 0.1, vol: 0.07 } },
|
|
14615
|
+
gravel: { heel: { f: 80, d: 0.05, vol: 0.4 }, roll: { type: "lowpass", f: 950, q: 0.8, d: 0.1, vol: 0.3 }, grains: { count: 8, f: 2600, q: 3, spread: 0.13, vol: 0.2 } },
|
|
14616
|
+
sand: { heel: { f: 65, d: 0.06, vol: 0.3 }, roll: { type: "bandpass", f: 1500, q: 0.6, d: 0.16, vol: 0.3 }, grains: { count: 5, f: 4800, q: 1.5, spread: 0.12, vol: 0.05 } },
|
|
14617
|
+
metal: { heel: { f: 120, d: 0.05, vol: 0.5 }, roll: { type: "bandpass", f: 2800, q: 1.5, d: 0.03, vol: 0.25 }, ring: [{ f: 420, d: 0.25, vol: 0.16 }, { f: 1130, d: 0.18, vol: 0.09 }, { f: 2470, d: 0.1, vol: 0.04 }] },
|
|
14618
|
+
water: { heel: { f: 70, d: 0.04, vol: 0.25 }, roll: { type: "bandpass", f: 3e3, q: 1.2, d: 0.1, vol: 0.18 }, splash: { f: 1100, f2: 380, d: 0.22, vol: 0.55 }, grains: { count: 4, f: 2400, q: 6, spread: 0.18, vol: 0.12 } },
|
|
14619
|
+
glass: { heel: { f: 90, d: 0.04, vol: 0.4 }, roll: { type: "highpass", f: 4500, q: 0.8, d: 0.06, vol: 0.2 }, grains: { count: 9, f: 6200, q: 5, spread: 0.12, vol: 0.22 }, ring: [{ f: 3900, d: 0.04, vol: 0.04 }, { f: 5300, d: 0.03, vol: 0.03 }] },
|
|
14620
|
+
carpet: { heel: { f: 70, d: 0.05, vol: 0.32 }, roll: { type: "lowpass", f: 420, q: 0.7, d: 0.07, vol: 0.3 } }
|
|
14621
|
+
};
|
|
14622
|
+
var GAITS = {
|
|
14623
|
+
walk: { vol: 1, bright: 1, toe: 0.075, short: 1 },
|
|
14624
|
+
run: { vol: 1.35, bright: 1.25, toe: 0.035, short: 0.75 }
|
|
14625
|
+
};
|
|
14626
|
+
var grainLayers = (g, take, start, scale) => Array.from(
|
|
14627
|
+
{ length: g.count },
|
|
14628
|
+
(_, i) => sceneNoise({ type: "bandpass", f: [g.f * take.f * 0.85, g.f * take.f * 1.15], q: g.q, d: [8e-3, 0.02], vol: g.vol * scale * take.v, delay: start + (i + 0.5) / g.count * g.spread * take.t })
|
|
14629
|
+
);
|
|
14630
|
+
var ringLayers = (rings, take, scale, at3 = 0) => rings.map((r) => sceneTone({ f: r.f * take.f, d: r.d * take.t, vol: r.vol * scale * take.v, delay: at3 }));
|
|
14631
|
+
function footLayers(surface, take, gait, weight = 1) {
|
|
14632
|
+
const s = SURFACES[surface];
|
|
14633
|
+
const toe = gait.toe * take.t;
|
|
14634
|
+
return [
|
|
14635
|
+
sceneTone({ f: s.heel.f * take.f * weight ** -0.3, f2: s.heel.f * take.f * 0.6, d: s.heel.d * gait.short * take.t * weight ** 0.5, vol: s.heel.vol * gait.vol * take.v * weight ** 0.5 }),
|
|
14636
|
+
sceneNoise({ type: "lowpass", f: 500 * gait.bright, d: 0.02 * take.t, vol: 0.18 * gait.vol * take.v * weight ** 0.5 }),
|
|
14637
|
+
sceneNoise({ type: s.roll.type, f: [s.roll.f * take.f * gait.bright * 0.92, s.roll.f * take.f * gait.bright * 1.08], q: s.roll.q, d: s.roll.d * gait.short * take.t * weight ** 0.3, vol: s.roll.vol * gait.vol * take.v, delay: toe }),
|
|
14638
|
+
...s.grains ? grainLayers(s.grains, take, toe * 0.5, gait.vol * weight ** 0.5) : [],
|
|
14639
|
+
...s.ring ? ringLayers(s.ring, take, gait.vol * weight ** 0.5) : [],
|
|
14640
|
+
...s.splash ? [sceneNoise({ type: "bandpass", f: s.splash.f * take.f, f2: s.splash.f2 * take.f, q: 1.4, d: s.splash.d * take.t * weight ** 0.4, vol: s.splash.vol * gait.vol * take.v * weight ** 0.5 })] : []
|
|
14641
|
+
];
|
|
14642
|
+
}
|
|
14643
|
+
var quieter = (level, layers) => layers.map((layer2) => {
|
|
14644
|
+
const gain = layer2.gain ?? 1;
|
|
14645
|
+
if (typeof gain === "number") return { ...layer2, gain: gain * level };
|
|
14646
|
+
if (Array.isArray(gain)) return { ...layer2, gain: [gain[0] * level, gain[1] * level] };
|
|
14647
|
+
return layer2;
|
|
14648
|
+
});
|
|
14649
|
+
var recipe = (id, layers, falloff, bus = "world") => ({ id, bus, pan: true, layers, falloff, sources: ["sdk"] });
|
|
14650
|
+
var takeId = (id, index) => `${id}_${String(index + 1).padStart(2, "0")}`;
|
|
14651
|
+
function takes(id, layersOf, falloff, rate = [0.96, 1.04], bus) {
|
|
14652
|
+
const sounds = TAKES.map((take, index) => recipe(takeId(id, index), layersOf(take, index), falloff, bus));
|
|
14653
|
+
return { sounds, set: { id, variants: sounds.map((sound) => sound.id), rate } };
|
|
14654
|
+
}
|
|
14655
|
+
var footstep = (surface, gait) => takes(footstepSoundId(surface, gait), (take) => footLayers(surface, take, GAITS[gait]), FOOTSTEP_FALLOFF);
|
|
14656
|
+
var LANDINGS = { light: { weight: 1.4, cloth: 0.12 }, heavy: { weight: 2.4, cloth: 0.22 } };
|
|
14657
|
+
var LANDING_LEVEL = 0.65;
|
|
14658
|
+
var landing = (surface, landing2) => takes(
|
|
14659
|
+
`land_${surface}_${landing2}`,
|
|
14660
|
+
(take) => quieter(LANDING_LEVEL, [
|
|
14661
|
+
// both feet, a hair apart, then the knees taking the weight (a low body thump) and the clothes settling
|
|
14662
|
+
...footLayers(surface, take, GAITS.run, LANDINGS[landing2].weight),
|
|
14663
|
+
...footLayers(surface, { ...take, v: take.v * 0.6 }, GAITS.run, LANDINGS[landing2].weight * 0.6).map((layer2) => ({ ...layer2, at: (typeof layer2.at === "number" ? layer2.at : 0) + 0.018 * take.t })),
|
|
14664
|
+
sceneTone({ f: 60 * take.f, f2: 38, d: 0.14 * LANDINGS[landing2].weight * take.t, vol: 0.22 * LANDINGS[landing2].weight ** 0.7 * take.v, delay: 0.02 }),
|
|
14665
|
+
sceneNoise({ type: "bandpass", f: [1800, 2600], q: 0.8, d: 0.18, vol: LANDINGS[landing2].cloth * take.v, delay: 0.04 })
|
|
14666
|
+
]),
|
|
14667
|
+
FOOTSTEP_FALLOFF
|
|
14668
|
+
);
|
|
14669
|
+
var whoosh = (blow3, from, to, d, vol, q = 1.6) => (take) => [
|
|
14670
|
+
// the air: a band sweeping up as the limb speeds up; it peaks `SWING_LEAD_SECONDS` in, the moment before the contact, then dies away
|
|
14671
|
+
sceneNoise({ type: "bandpass", f: from * take.f, f2: to * take.f, q, a: SWING_LEAD_SECONDS[blow3], d: d * take.t, vol: vol * take.v }),
|
|
14672
|
+
sceneNoise({ type: "lowpass", f: 600 * take.f, a: SWING_LEAD_SECONDS[blow3], d: d * take.t * 0.8, vol: vol * 0.35 * take.v })
|
|
14673
|
+
];
|
|
14674
|
+
var fleshHit = (o) => (take) => [
|
|
14675
|
+
sceneNoise({ type: "bandpass", f: o.slapF * take.f, q: 1.2, d: 0.03 * take.t, vol: o.slap * take.v }),
|
|
14676
|
+
sceneNoise({ type: "highpass", f: 3500 * take.f, d: 0.012, vol: o.slap * 0.4 * take.v }),
|
|
14677
|
+
sceneTone({ f: o.thudF * take.f, f2: o.thudF * 0.5, d: o.thudD * take.t, vol: o.thud * take.v }),
|
|
14678
|
+
sceneNoise({ type: "lowpass", f: 600 * take.f, d: o.thudD * 1.2 * take.t, vol: o.thud * 0.5 * take.v }),
|
|
14679
|
+
...o.crack ? [sceneNoise({ type: "highpass", f: 2500, d: 0.015, vol: o.crack * take.v, delay: 6e-3 }), sceneTone({ wave: "square", f: 900 * take.f, f2: 300, d: 0.02, vol: o.crack * 0.15 * take.v, delay: 6e-3 })] : [],
|
|
14680
|
+
...o.wet ? [sceneNoise({ type: "bandpass", f: 1400 * take.f, f2: 500, q: 2.5, d: 0.12 * take.t, vol: o.wet * take.v, delay: 0.01 })] : []
|
|
14681
|
+
];
|
|
14682
|
+
var knock = (o) => (take) => [
|
|
14683
|
+
sceneNoise({ type: "bandpass", f: o.clickF * take.f, q: 1.5, d: 0.018, vol: o.click * take.v }),
|
|
14684
|
+
...o.tones.map(([f, d, vol]) => sceneTone({ f: f * take.f, d: d * take.t, vol: vol * take.v }))
|
|
14685
|
+
];
|
|
14686
|
+
var voice = (o) => (take) => [
|
|
14687
|
+
sceneVoice({ f0: o.f0 * take.f, f1: o.f1 * take.f, d: o.d * take.t, fA: o.fA * take.f, fB: o.fB * take.f, noise: o.noise, vol: o.vol * take.v })
|
|
14688
|
+
];
|
|
14689
|
+
var BODY = {
|
|
14690
|
+
jump: (take) => [
|
|
14691
|
+
sceneNoise({ type: "bandpass", f: 900 * take.f, f2: 1700 * take.f, q: 1, a: 0.03, d: 0.12, vol: 0.18 * take.v }),
|
|
14692
|
+
sceneNoise({ type: "bandpass", f: 2200 * take.f, q: 0.8, d: 0.04, vol: 0.12 * take.v }),
|
|
14693
|
+
sceneTone({ f: 85 * take.f, f2: 60, d: 0.04, vol: 0.2 * take.v })
|
|
14694
|
+
],
|
|
14695
|
+
cloth_rustle: (take) => [
|
|
14696
|
+
{ ...sceneNoise({ type: "bandpass", f: 2400 * take.f, q: 0.8, a: 0.06, d: 0.22 * take.t, vol: 0.12 * take.v }), modulators: [{ target: "gain", wave: "triangle", rate: [9, 14], depth: 0.5 }] }
|
|
14697
|
+
],
|
|
14698
|
+
body_fall: (take) => [
|
|
14699
|
+
sceneTone({ f: 58 * take.f, f2: 35, d: 0.28 * take.t, vol: 0.9 * take.v }),
|
|
14700
|
+
sceneNoise({ type: "lowpass", f: 480 * take.f, d: 0.3 * take.t, vol: 0.55 * take.v }),
|
|
14701
|
+
sceneNoise({ type: "bandpass", f: 1300 * take.f, q: 1, d: 0.05, vol: 0.3 * take.v }),
|
|
14702
|
+
// the limbs a moment later, and the clothes
|
|
14703
|
+
sceneTone({ f: 75 * take.f, f2: 45, d: 0.12, vol: 0.4 * take.v, delay: 0.13 * take.t }),
|
|
14704
|
+
sceneNoise({ type: "bandpass", f: 2200, q: 0.8, d: 0.2, vol: 0.12 * take.v, delay: 0.05 })
|
|
14705
|
+
],
|
|
14706
|
+
fall_damage: (take) => [
|
|
14707
|
+
...BODY.body_fall(take),
|
|
14708
|
+
sceneNoise({ type: "highpass", f: 2800, d: 0.02, vol: 0.35 * take.v, delay: 0.01 }),
|
|
14709
|
+
...voice({ f0: 170, f1: 110, d: 0.32, fA: 650, fB: 1150, noise: 0.25, vol: 0.32 })(take).map((layer2) => ({ ...layer2, at: 0.08 }))
|
|
14710
|
+
],
|
|
14711
|
+
player_hurt: voice({ f0: 150, f1: 105, d: 0.26, fA: 620, fB: 1100, noise: 0.3, vol: 0.35 }),
|
|
14712
|
+
player_breath_run: (take) => [
|
|
14713
|
+
sceneNoise({ type: "bandpass", f: 1200 * take.f, q: 1, a: 0.08, d: 0.22 * take.t, vol: 0.25 * take.v }),
|
|
14714
|
+
sceneNoise({ type: "bandpass", f: 900 * take.f, q: 1, a: 0.06, d: 0.26 * take.t, vol: 0.3 * take.v, delay: 0.3 * take.t })
|
|
14715
|
+
],
|
|
14716
|
+
player_death: voice({ f0: 135, f1: 68, d: 1, fA: 560, fB: 980, noise: 0.35, vol: 0.4 }),
|
|
14717
|
+
bite: (take) => [
|
|
14718
|
+
sceneNoise({ type: "highpass", f: 2600, d: 0.015, vol: 0.4 * take.v }),
|
|
14719
|
+
sceneNoise({ type: "bandpass", f: 1600 * take.f, f2: 450, q: 2.2, d: 0.16 * take.t, vol: 0.55 * take.v, delay: 0.01 }),
|
|
14720
|
+
sceneNoise({ type: "bandpass", f: 3200 * take.f, f2: 1800, q: 1.2, d: 0.22 * take.t, vol: 0.25 * take.v, delay: 0.09 }),
|
|
14721
|
+
sceneTone({ f: 120 * take.f, f2: 70, d: 0.06, vol: 0.3 * take.v })
|
|
14722
|
+
],
|
|
14723
|
+
grab: (take) => [
|
|
14724
|
+
sceneNoise({ type: "bandpass", f: 1900 * take.f, q: 0.9, a: 4e-3, d: 0.13 * take.t, vol: 0.32 * take.v }),
|
|
14725
|
+
sceneTone({ f: 95 * take.f, f2: 60, d: 0.07, vol: 0.3 * take.v }),
|
|
14726
|
+
sceneNoise({ type: "lowpass", f: 700, d: 0.08, vol: 0.2 * take.v })
|
|
14727
|
+
],
|
|
14728
|
+
punch_swing: whoosh("punch", 450, 1500, 0.16, 0.9),
|
|
14729
|
+
punch_hit_body: fleshHit({ slap: 0.55, slapF: 1500, thud: 0.6, thudF: 115, thudD: 0.09 }),
|
|
14730
|
+
punch_hit_face: fleshHit({ slap: 0.75, slapF: 2300, thud: 0.45, thudF: 150, thudD: 0.07, crack: 0.25 }),
|
|
14731
|
+
punch_hit_object: knock({ clickF: 1800, click: 0.3, tones: [[210, 0.04, 0.25]] }),
|
|
14732
|
+
kick_swing: whoosh("kick", 280, 950, 0.22, 1, 1.3),
|
|
14733
|
+
kick_hit: fleshHit({ slap: 0.45, slapF: 1100, thud: 0.85, thudF: 82, thudD: 0.14 }),
|
|
14734
|
+
kick_hit_object: knock({ clickF: 1e3, click: 0.3, tones: [[92, 0.08, 0.5]] }),
|
|
14735
|
+
shove_two_hands: (take) => [
|
|
14736
|
+
sceneNoise({ type: "bandpass", f: 950 * take.f, q: 0.9, a: 0.01, d: 0.13 * take.t, vol: 0.38 * take.v }),
|
|
14737
|
+
sceneTone({ f: 100 * take.f, f2: 60, d: 0.1 * take.t, vol: 0.45 * take.v }),
|
|
14738
|
+
sceneNoise({ type: "bandpass", f: 2300, q: 0.8, d: 0.16, vol: 0.12 * take.v, delay: 0.03 })
|
|
14739
|
+
],
|
|
14740
|
+
knife_swing: whoosh("blade", 1900, 4600, 0.12, 1.1, 2.2),
|
|
14741
|
+
knife_stab: fleshHit({ slap: 0.2, slapF: 1200, thud: 0.4, thudF: 160, thudD: 0.06, wet: 0.5 }),
|
|
14742
|
+
knife_slash: (take) => [
|
|
14743
|
+
sceneNoise({ type: "highpass", f: 3200 * take.f, f2: 1800, d: 0.13 * take.t, vol: 0.35 * take.v }),
|
|
14744
|
+
sceneNoise({ type: "bandpass", f: 1300 * take.f, f2: 600, q: 2.5, d: 0.12 * take.t, vol: 0.35 * take.v, delay: 0.02 })
|
|
14745
|
+
],
|
|
14746
|
+
knife_hit_object: knock({ clickF: 3200, click: 0.25, tones: [[2800, 0.08, 0.07], [4150, 0.06, 0.05]] }),
|
|
14747
|
+
blunt_swing: whoosh("blunt", 220, 760, 0.28, 1.1, 1.5),
|
|
14748
|
+
blunt_hit: fleshHit({ slap: 0.35, slapF: 900, thud: 1, thudF: 70, thudD: 0.18, crack: 0.3 }),
|
|
14749
|
+
blunt_hit_object: knock({ clickF: 1400, click: 0.35, tones: [[300, 0.12, 0.25], [760, 0.08, 0.12]] }),
|
|
14750
|
+
axe_swing: whoosh("chop", 190, 620, 0.3, 1.2, 1.4),
|
|
14751
|
+
axe_chop: fleshHit({ slap: 0.3, slapF: 1400, thud: 0.75, thudF: 140, thudD: 0.1, crack: 0.35, wet: 0.45 }),
|
|
14752
|
+
axe_hit_object: knock({ clickF: 2200, click: 0.4, tones: [[1500, 0.15, 0.1], [2300, 0.1, 0.06], [140, 0.1, 0.35]] })
|
|
14753
|
+
};
|
|
14754
|
+
var BODY_BUS = { player_hurt: "voices", player_death: "voices", player_breath_run: "voices", fall_damage: "voices" };
|
|
14755
|
+
var built = [
|
|
14756
|
+
...SURFACE_IDS.flatMap((surface) => GAIT_IDS.map((gait) => footstep(surface, gait))),
|
|
14757
|
+
...SURFACE_IDS.flatMap((surface) => LANDING_IDS.map((l) => landing(surface, l))),
|
|
14758
|
+
...BODY_SOUND_IDS.map((id) => takes(id, BODY[id], BODY_FALLOFF, id.endsWith("_swing") ? [0.94, 1.06] : [0.96, 1.04], BODY_BUS[id]))
|
|
14759
|
+
];
|
|
14760
|
+
var BODY_FOLEY_BANK = {
|
|
14761
|
+
id: "body_foley",
|
|
14762
|
+
sounds: built.flatMap((entry) => entry.sounds),
|
|
14763
|
+
sets: built.map((entry) => entry.set)
|
|
14267
14764
|
};
|
|
14765
|
+
var BODY_FOLEY_SET_IDS = [...FOOTSTEP_SOUND_IDS, ...LAND_SOUND_IDS, ...BODY_SOUND_IDS];
|
|
14268
14766
|
|
|
14269
14767
|
// src/audio/recipes.ts
|
|
14270
14768
|
var scene = (id, layers, line, vars) => sceneRecipe(id, layers, line, vars ? { vars } : {});
|