@adatechnology/game-vfx-three 0.2.0 → 0.3.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 +3 -1
- package/dist/index.global.js +199 -124
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +102 -71
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as THREE_NS from 'three';
|
|
2
|
-
import { EffectSink, TestableModule } from '@adatechnology/game-contracts';
|
|
2
|
+
import { EffectSink, WarmUpProbes, TestableModule } from '@adatechnology/game-contracts';
|
|
3
3
|
import { VfxWorld, ScreenState, VfxOptions, VfxProfileName, EffectDef, ExplosionDef, GroundField, SpriteKindSystem, MarkEvent } from '@adatechnology/game-vfx';
|
|
4
4
|
|
|
5
5
|
/** The only three.js members the renderer needs at runtime. Pass your own `THREE` instance (the whole namespace works). */
|
|
@@ -56,6 +56,8 @@ interface VfxRenderer extends EffectSink {
|
|
|
56
56
|
readonly world: VfxWorld;
|
|
57
57
|
/** Steps the world, then redraws everything from it. Call once per frame (a shared world is stepped here, nowhere else). */
|
|
58
58
|
update(dt: number): void;
|
|
59
|
+
/** Meshes that use the materials of the solid particles, to render once so that the first effect does not build their shader programs in the middle of a frame. */
|
|
60
|
+
warmUpProbes(): WarmUpProbes;
|
|
59
61
|
/** Shakes the camera for the render that follows (the Lab's L1092) and answers the function that puts it back. */
|
|
60
62
|
applyCamera(): () => void;
|
|
61
63
|
stats(): VfxRendererStats;
|
package/dist/index.global.js
CHANGED
|
@@ -3098,6 +3098,17 @@ var GameVfxThree = (() => {
|
|
|
3098
3098
|
])
|
|
3099
3099
|
]
|
|
3100
3100
|
};
|
|
3101
|
+
var FUEL_LEAK_ONLY = {
|
|
3102
|
+
id: "fuel_leak_only",
|
|
3103
|
+
pageId: "vazamento",
|
|
3104
|
+
team: "vehicles",
|
|
3105
|
+
budget: { particles: 200, lights: 0 },
|
|
3106
|
+
loop: 11,
|
|
3107
|
+
view: FUEL_LEAK.view,
|
|
3108
|
+
setup: FUEL_LEAK.setup,
|
|
3109
|
+
timeline: [decal(POOL[0], 0, POOL[2], 2.2, 2040368, { grow: 3, life: 60, ar: 0.6, fluid: "oil" })],
|
|
3110
|
+
tick: [emit("drip", "normal", 12, { p: TANK, box: [0.05, 0, 0.05], sp: 0, life: 1, size: 0.03, c0: 5921402, a: 0.9, g: 9.8, fi: 0, gnd: 1 })]
|
|
3111
|
+
};
|
|
3101
3112
|
var CAR_EXPLOSION = {
|
|
3102
3113
|
id: "car_explosion",
|
|
3103
3114
|
pageId: "explosao",
|
|
@@ -3451,6 +3462,8 @@ var GameVfxThree = (() => {
|
|
|
3451
3462
|
];
|
|
3452
3463
|
var PI = Math.PI;
|
|
3453
3464
|
var SMOKE = { color: 9079430, width: 10, height: 8, gravity: -0.08, drag: 2.2, growth: 2.1 };
|
|
3465
|
+
var DEFAULT_SMOKE = { puffs: 2, radius: 0.01, opacity: 0.14, life: 0.7, drift: 0.2, rise: 0.06, growth: 2.1, flash: 1 };
|
|
3466
|
+
var DEFAULT_SPREAD = { drift: 0.1, rise: 0.04, side: 0.06 };
|
|
3454
3467
|
var FLASH_SECONDS = 0.055;
|
|
3455
3468
|
var FLASH_RAMP = [0.6, 0.9];
|
|
3456
3469
|
var FLASH_OPACITY = 0.95;
|
|
@@ -3458,8 +3471,10 @@ var GameVfxThree = (() => {
|
|
|
3458
3471
|
var FLAME_ADDITIVE = 3;
|
|
3459
3472
|
var ORIGIN2 = [0, 0, 0];
|
|
3460
3473
|
var SIZE = variable("size");
|
|
3474
|
+
var FLASH_SCALE = variable("flash");
|
|
3475
|
+
var fallback = (name, defaultValue) => ite(ne(variable(name), void 0), variable(name), defaultValue);
|
|
3461
3476
|
var wide = (width) => mul(sub(rand(), 0.5), width);
|
|
3462
|
-
var puff2 = (point, velocity, world, radius, life, opacity) => mesh({
|
|
3477
|
+
var puff2 = (point, velocity, world, radius, life, opacity, growth = SMOKE.growth) => mesh({
|
|
3463
3478
|
shape: "sphere",
|
|
3464
3479
|
p: point,
|
|
3465
3480
|
v: velocity,
|
|
@@ -3474,7 +3489,7 @@ var GameVfxThree = (() => {
|
|
|
3474
3489
|
lit: true,
|
|
3475
3490
|
g: SMOKE.gravity,
|
|
3476
3491
|
drag: SMOKE.drag,
|
|
3477
|
-
grow:
|
|
3492
|
+
grow: growth
|
|
3478
3493
|
});
|
|
3479
3494
|
var flameBit = (point, velocity, world, radius, life) => [
|
|
3480
3495
|
set("bitPoint", point),
|
|
@@ -3507,37 +3522,55 @@ var GameVfxThree = (() => {
|
|
|
3507
3522
|
id: "viewmodel_muzzle_flash",
|
|
3508
3523
|
pageId: "event:viewmodel_muzzle_flash",
|
|
3509
3524
|
team: "weapons",
|
|
3510
|
-
budget: { particles:
|
|
3525
|
+
budget: { particles: 14, lights: 1 },
|
|
3511
3526
|
loop: 0.1,
|
|
3527
|
+
setup: [
|
|
3528
|
+
set("size", fallback("size", 1)),
|
|
3529
|
+
set("puffs", fallback("puffs", DEFAULT_SMOKE.puffs)),
|
|
3530
|
+
set("radius", fallback("radius", DEFAULT_SMOKE.radius)),
|
|
3531
|
+
set("opacity", fallback("opacity", DEFAULT_SMOKE.opacity)),
|
|
3532
|
+
set("life", fallback("life", DEFAULT_SMOKE.life)),
|
|
3533
|
+
set("drift", fallback("drift", DEFAULT_SMOKE.drift)),
|
|
3534
|
+
set("rise", fallback("rise", DEFAULT_SMOKE.rise)),
|
|
3535
|
+
set("growth", fallback("growth", DEFAULT_SMOKE.growth)),
|
|
3536
|
+
set("flash", fallback("flash", DEFAULT_SMOKE.flash)),
|
|
3537
|
+
set("driftSpread", fallback("driftSpread", DEFAULT_SPREAD.drift)),
|
|
3538
|
+
set("riseSpread", fallback("riseSpread", DEFAULT_SPREAD.rise)),
|
|
3539
|
+
set("sideSpread", fallback("sideSpread", DEFAULT_SPREAD.side)),
|
|
3540
|
+
set("lifeSpread", ite(ne(variable("lifeSpread"), void 0), variable("lifeSpread"), mul(variable("life"), 0.3)))
|
|
3541
|
+
],
|
|
3512
3542
|
timeline: [
|
|
3513
3543
|
set("angles", []),
|
|
3514
3544
|
repeat(4, append("angles", add(div(mul(INDEX, PI), 2), mul(rand(), 0.6)))),
|
|
3515
3545
|
set("roll", mul(rand(), PI)),
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3546
|
+
when(gt(FLASH_SCALE, 0), [
|
|
3547
|
+
flash(ORIGIN2, 16756832, mul(4, SIZE, FLASH_SCALE), FLASH_SECONDS, mul(2, SIZE, FLASH_SCALE), true, true),
|
|
3548
|
+
flashPiece("cone_open", mul(0.013, SIZE, FLASH_SCALE), mul(0.085, SIZE, FLASH_SCALE), 10, [mul(0.042, SIZE, FLASH_SCALE), 0, 0], [0, 0, -PI / 2], 16757575),
|
|
3549
|
+
repeat(
|
|
3550
|
+
4,
|
|
3551
|
+
set("angle", pick(variable("angles"), INDEX)),
|
|
3552
|
+
flashPiece(
|
|
3553
|
+
"cone",
|
|
3554
|
+
mul(5e-3, SIZE, FLASH_SCALE),
|
|
3555
|
+
mul(0.04, SIZE, FLASH_SCALE),
|
|
3556
|
+
6,
|
|
3557
|
+
[mul(0.012, SIZE, FLASH_SCALE), mul(cos(variable("angle")), 0.016, SIZE, FLASH_SCALE), mul(sin(variable("angle")), 0.016, SIZE, FLASH_SCALE)],
|
|
3558
|
+
[variable("angle"), 0, -PI / 2],
|
|
3559
|
+
16757575
|
|
3560
|
+
)
|
|
3561
|
+
),
|
|
3562
|
+
flashPiece("sphere", mul(0.016, SIZE, FLASH_SCALE), void 0, 12, [mul(8e-3, SIZE, FLASH_SCALE), 0, 0], [0, 0, 0], 16773568)
|
|
3563
|
+
]),
|
|
3532
3564
|
repeat(
|
|
3533
|
-
|
|
3565
|
+
variable("puffs"),
|
|
3534
3566
|
puff2(
|
|
3535
3567
|
ORIGIN2,
|
|
3536
|
-
[add(
|
|
3568
|
+
[add(variable("drift"), mul(rand(), variable("driftSpread"))), add(variable("rise"), mul(rand(), variable("riseSpread"))), mul(sub(rand(), 0.5), variable("sideSpread"))],
|
|
3537
3569
|
[0, 0, 0],
|
|
3538
|
-
mul(
|
|
3539
|
-
add(
|
|
3540
|
-
|
|
3570
|
+
mul(variable("radius"), SIZE),
|
|
3571
|
+
add(variable("life"), mul(rand(), variable("lifeSpread"))),
|
|
3572
|
+
variable("opacity"),
|
|
3573
|
+
variable("growth")
|
|
3541
3574
|
)
|
|
3542
3575
|
)
|
|
3543
3576
|
]
|
|
@@ -3585,9 +3618,20 @@ var GameVfxThree = (() => {
|
|
|
3585
3618
|
]
|
|
3586
3619
|
};
|
|
3587
3620
|
var VIEWMODEL_EFFECTS = [MUZZLE_FLASH2, SMOKE_PUFF, BACKBLAST, FLAME_JET];
|
|
3621
|
+
var aliasOf = (base, id, changes) => ({ ...base, id, pageId: `vehicle:${id}`, anchor: CAR_ORIGIN2, ...changes });
|
|
3622
|
+
var VEHICLE_EFFECTS = [
|
|
3623
|
+
/** The engine on fire: `engine_smoke` at its worst condition (black smoke, flames in the hood, a light). */
|
|
3624
|
+
aliasOf(ENGINE_SMOKE, "vehicle_fire", { params: { id: "condition", options: ["100", "60", "30", "10"], default: 3 }, loop: 12 }),
|
|
3625
|
+
/** The exhaust of a car under way: `engine_smoke` in the best condition, for half a second so a moving car asks again. */
|
|
3626
|
+
aliasOf(ENGINE_SMOKE, "vehicle_exhaust", { params: { id: "condition", options: ["100", "60", "30", "10"], default: 0 }, loop: 0.5 }),
|
|
3627
|
+
/** A hole in the tank: the page's petrol leak without the fire and the blast (the pool and the drips). A normal bullet does only this. */
|
|
3628
|
+
aliasOf(FUEL_LEAK_ONLY, "vehicle_leak", {}),
|
|
3629
|
+
/** 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. */
|
|
3630
|
+
aliasOf(FUEL_LEAK, "vehicle_leak_ignited", {})
|
|
3631
|
+
];
|
|
3588
3632
|
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];
|
|
3589
3633
|
var WORKSHOP_EFFECTS = Object.fromEntries(ALL_EFFECTS.map((effect) => [effect.id, effect]));
|
|
3590
|
-
var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS].map((effect) => [effect.id, effect])) };
|
|
3634
|
+
var GAME_EFFECTS = { ...WORKSHOP_EFFECTS, ...Object.fromEntries([...EVENT_EFFECTS, ...VIEWMODEL_EFFECTS, ...VEHICLE_EFFECTS].map((effect) => [effect.id, effect])) };
|
|
3591
3635
|
var LEHMER_MULTIPLIER = 16807;
|
|
3592
3636
|
var LEHMER_MODULUS = 2147483647;
|
|
3593
3637
|
var HASH_MIX = 2654435761;
|
|
@@ -7229,7 +7273,7 @@ var GameVfxThree = (() => {
|
|
|
7229
7273
|
blood_hit: {
|
|
7230
7274
|
name: "Blood by infection age",
|
|
7231
7275
|
short: "A mist on the way in, a jet on the way out, a pool that grows. Age changes colour and amount.",
|
|
7232
|
-
cost: "\u2264 70
|
|
7276
|
+
cost: "\u2264 70 particles",
|
|
7233
7277
|
costNote: "1 pool \xB7 6 splashes",
|
|
7234
7278
|
paramLabel: "Age",
|
|
7235
7279
|
when: "Every bullet or blow that hits a zombie. The blood value comes from the bestiary age: 1.0 \xB7 0.7 \xB7 0.35 \xB7 0.1.",
|
|
@@ -7240,7 +7284,7 @@ var GameVfxThree = (() => {
|
|
|
7240
7284
|
headshot: {
|
|
7241
7285
|
name: "Headshot",
|
|
7242
7286
|
short: "The head bursts, the eyes go out on the same frame and the body drops.",
|
|
7243
|
-
cost: "\u2264 70
|
|
7287
|
+
cost: "\u2264 70 particles",
|
|
7244
7288
|
costNote: "8 debris \xB7 1 pool",
|
|
7245
7289
|
when: "A head hit that kills (bestiary rule: a head kills at once).",
|
|
7246
7290
|
look: "A red mist around the head, a jet of drops backwards, 6 to 8 pieces of bone and skin, a swap to the burst-neck mesh. The eye glow goes out on the same frame, which is the most readable signal from afar.",
|
|
@@ -7250,7 +7294,7 @@ var GameVfxThree = (() => {
|
|
|
7250
7294
|
dismember: {
|
|
7251
7295
|
name: "Dismemberment",
|
|
7252
7296
|
short: "The arm comes off whole with physics and the shoulder gushes for half a second.",
|
|
7253
|
-
cost: "\u2264 60
|
|
7297
|
+
cost: "\u2264 60 particles",
|
|
7254
7298
|
costNote: "1 limb \xB7 4 splashes",
|
|
7255
7299
|
when: "Damage to a limb above the threshold (multipliers by region from the bestiary) or an axe blow.",
|
|
7256
7300
|
look: "The limb becomes a loose piece with physics and bounces. A short pulsing jet from the shoulder, drops that form a trail to the pool. The zombie staggers but keeps walking.",
|
|
@@ -7260,7 +7304,7 @@ var GameVfxThree = (() => {
|
|
|
7260
7304
|
bloater_cloud: {
|
|
7261
7305
|
name: "Bloater cloud",
|
|
7262
7306
|
short: "The belly pulses for 0.5 s, bursts and leaves 4 m of gas for 8 s.",
|
|
7263
|
-
cost: "\u2264 180
|
|
7307
|
+
cost: "\u2264 180 particles",
|
|
7264
7308
|
costNote: "server entity",
|
|
7265
7309
|
when: "Death of the Bloater, by any cause.",
|
|
7266
7310
|
look: "A 0.5 s warning with the belly swelling, a burst with pieces and a greenish jet, and a low moss-green cloud that undulates on the ground for 8 s. Loose green glows inside the cloud mark the area.",
|
|
@@ -7270,7 +7314,7 @@ var GameVfxThree = (() => {
|
|
|
7270
7314
|
screamer_shriek: {
|
|
7271
7315
|
name: "Screamer shriek",
|
|
7272
7316
|
short: "A 1.5 s wind-up with steam, a wave on the ground and the horde's eyes light up.",
|
|
7273
|
-
cost: "\u2264 150
|
|
7317
|
+
cost: "\u2264 150 particles",
|
|
7274
7318
|
costNote: "3 rings \xB7 aberration",
|
|
7275
7319
|
when: "The Screamer spots the player and starts the shriek (1.5 s wind-up, calls everyone within 150 m).",
|
|
7276
7320
|
look: "Head back and steam from the mouth during the wind-up. On the shriek, three pressure rings on the ground, dust raised in the radius, a short chromatic aberration on the screen of whoever is near, and the eyes of nearby zombies flare when they hear.",
|
|
@@ -7290,7 +7334,7 @@ var GameVfxThree = (() => {
|
|
|
7290
7334
|
corpse_decay: {
|
|
7291
7335
|
name: "Corpse: darken and dissolve",
|
|
7292
7336
|
short: "15 minutes in 8 seconds: fall, darken, flies and turn to dust.",
|
|
7293
|
-
cost: "\u2264 40
|
|
7337
|
+
cost: "\u2264 40 particles",
|
|
7294
7338
|
costNote: "pool of 400 bodies",
|
|
7295
7339
|
when: "Zombie death. Bestiary timeline: ragdoll 3 s, frozen mesh, darken, dissolve between 14 and 15 min.",
|
|
7296
7340
|
look: "The skin loses colour until almost black, the pool dries to brown, flies appear from minute 5 and in the last minute the body sinks and gives off dark dust.",
|
|
@@ -7300,7 +7344,7 @@ var GameVfxThree = (() => {
|
|
|
7300
7344
|
reanimation: {
|
|
7301
7345
|
name: "Reanimation",
|
|
7302
7346
|
short: "Veins light up, spasms, the eyes switch on and the body gets up.",
|
|
7303
|
-
cost: "\u2264 30
|
|
7347
|
+
cost: "\u2264 30 particles",
|
|
7304
7348
|
costNote: "emissive in the shader",
|
|
7305
7349
|
when: "End of infection of a player or NPC: reanimates in 2 to 6 min as Fresh (70% Slow, 30% Runner).",
|
|
7306
7350
|
look: "Green pulses through the skin (emissive in the shader, not particles), spasms in the arms and head, the eyes light up from the inside out and the body stands. Breath as steam on the first shriek.",
|
|
@@ -7310,7 +7354,7 @@ var GameVfxThree = (() => {
|
|
|
7310
7354
|
muzzle_flash: {
|
|
7311
7355
|
name: "Muzzle flash",
|
|
7312
7356
|
short: "Core, petals and smoke scaled by weapon. The Barrett raises dust.",
|
|
7313
|
-
cost: "\u2264 60
|
|
7357
|
+
cost: "\u2264 60 particles",
|
|
7314
7358
|
costNote: "1 light \xB7 50 ms",
|
|
7315
7359
|
paramLabel: "Weapon",
|
|
7316
7360
|
when: "Every shot. In first person it uses the weapon model; from afar it is only the core sprite.",
|
|
@@ -7321,7 +7365,7 @@ var GameVfxThree = (() => {
|
|
|
7321
7365
|
impact: {
|
|
7322
7366
|
name: "Impact by material",
|
|
7323
7367
|
short: "Concrete, metal, wood, glass, flesh and water: each with its own response.",
|
|
7324
|
-
cost: "\u2264 40
|
|
7368
|
+
cost: "\u2264 40 particles",
|
|
7325
7369
|
costNote: "1 hole",
|
|
7326
7370
|
paramLabel: "Material",
|
|
7327
7371
|
when: "Every bullet that hits something. The material comes from the mesh surface tag.",
|
|
@@ -7332,7 +7376,7 @@ var GameVfxThree = (() => {
|
|
|
7332
7376
|
penetration: {
|
|
7333
7377
|
name: "Bullet through the horde",
|
|
7334
7378
|
short: "Barrett tracer passing through 5 zombies, in 10x slow motion.",
|
|
7335
|
-
cost: "\u2264 250
|
|
7379
|
+
cost: "\u2264 250 particles",
|
|
7336
7380
|
costNote: "aggregate event",
|
|
7337
7381
|
when: "Weapon with penetration: the AK goes through 2, the Remington 700 through 3, the Barrett through 6 and cars.",
|
|
7338
7382
|
look: "A tracer with a hot-air trail, blood exiting each body in the order the bullet passes (each in the colour of its age), bodies falling one after another and a final impact on the concrete.",
|
|
@@ -7352,7 +7396,7 @@ var GameVfxThree = (() => {
|
|
|
7352
7396
|
molotov: {
|
|
7353
7397
|
name: "Molotov cocktail",
|
|
7354
7398
|
short: "A lit wick in the arc, glass breaking and a pool of fire that spreads.",
|
|
7355
|
-
cost: "\u2264 300
|
|
7399
|
+
cost: "\u2264 300 particles",
|
|
7356
7400
|
costNote: "2 lights \xB7 entity",
|
|
7357
7401
|
when: "A molotov hitting the ground or a zombie.",
|
|
7358
7402
|
look: "A bottle in an arc with the wick giving off flame and smoke. On impact, green shards and a splash of fire that opens from 0.5 m to 2.2 m in 0.8 s. Low flames, embers, dark smoke and a flickering light for 6 s, then embers and a burn mark.",
|
|
@@ -7362,7 +7406,7 @@ var GameVfxThree = (() => {
|
|
|
7362
7406
|
grenade: {
|
|
7363
7407
|
name: "Fragmentation grenade",
|
|
7364
7408
|
short: "A bounce on the asphalt, flash, shock wave, shrapnel and a ring of dust.",
|
|
7365
|
-
cost: "\u2264 400
|
|
7409
|
+
cost: "\u2264 400 particles",
|
|
7366
7410
|
costNote: "aggregate event",
|
|
7367
7411
|
when: "A grenade exploding (1.8 s fuse after the throw).",
|
|
7368
7412
|
look: "The grenade bounces twice. A layered explosion: a white flash that paints the screen, a short fireball, a shock ring on the ground and another facing the camera, bright shrapnel that bounces, a ring of low dust, grey smoke that rises and a burn mark. Zombies in the radius fall outwards.",
|
|
@@ -7372,7 +7416,7 @@ var GameVfxThree = (() => {
|
|
|
7372
7416
|
explosive_barrel: {
|
|
7373
7417
|
name: "Explosive barrel",
|
|
7374
7418
|
short: "A mushroom of fire, the lid flying, petrol burning on the ground and a column of smoke.",
|
|
7375
|
-
cost: "\u2264 450
|
|
7419
|
+
cost: "\u2264 450 particles",
|
|
7376
7420
|
costNote: "2 lights \xB7 fire entity",
|
|
7377
7421
|
when: "A red barrel with damage above the threshold, a shot or fire touching it.",
|
|
7378
7422
|
look: "Everything the grenade does, plus: a fireball that rises into a mushroom, the lid and red pieces flying high, three pools of petrol burning for 5 s and a black smoke column that serves as a landmark from afar.",
|
|
@@ -7392,7 +7436,7 @@ var GameVfxThree = (() => {
|
|
|
7392
7436
|
door_stages: {
|
|
7393
7437
|
name: "Door giving way in 3 stages",
|
|
7394
7438
|
short: "Blows shake dust from the frame, a plank cracks and the door falls.",
|
|
7395
|
-
cost: "\u2264 120
|
|
7439
|
+
cost: "\u2264 120 particles",
|
|
7396
7440
|
costNote: "3 stages",
|
|
7397
7441
|
when: "The horde hitting a door or barricade (the Brute does 3x damage). Stages: 66%, 33%, 0% health.",
|
|
7398
7442
|
look: "Each blow shakes the door and loosens dust from the frame and splinters. At stage 2 the middle plank cracks and falls. At 3 the door topples and raises a cloud of low dust, and the zombie comes out of the dark.",
|
|
@@ -7402,7 +7446,7 @@ var GameVfxThree = (() => {
|
|
|
7402
7446
|
building_collapse: {
|
|
7403
7447
|
name: "Collapse",
|
|
7404
7448
|
short: "Dust trickling from the awning, blocks falling and a cloud of dust rolling down the street.",
|
|
7405
|
-
cost: "\u2264 350
|
|
7449
|
+
cost: "\u2264 350 particles",
|
|
7406
7450
|
costNote: "26 debris",
|
|
7407
7451
|
when: "A structure that loses its health: explosion, vehicle impact, a big horde pushing (destruction of the map buildings).",
|
|
7408
7452
|
look: "Before: fine dust trickling and pebbles falling for 1 s, as a warning. Then: large and small blocks fall, the camera shakes and a cloud of grey-beige dust rolls along the ground and hangs in the air for 5 s, hiding zombies.",
|
|
@@ -7412,7 +7456,7 @@ var GameVfxThree = (() => {
|
|
|
7412
7456
|
block_wall: {
|
|
7413
7457
|
name: "Wall brought down with holes",
|
|
7414
7458
|
short: "A burst leaves holes and chips; the wall falls in blocks and the holes stay on them.",
|
|
7415
|
-
cost: "\u2264 300
|
|
7459
|
+
cost: "\u2264 300 particles",
|
|
7416
7460
|
costNote: "30 blocks \xB7 attached holes",
|
|
7417
7461
|
when: "A concrete wall, low wall or barricade that loses health from shots, explosion, the Brute or a car (map and arsenal destruction).",
|
|
7418
7462
|
look: "Each bullet opens a dark hole with a chipped rim, giving off dust and pebbles. When the wall gives way it splits into blocks that topple with physics and a cloud of dust rolls along the ground. The bullet holes are attached to the blocks: they stay on the fallen pieces, turned with them, and do not vanish when the wall breaks.",
|
|
@@ -7422,7 +7466,7 @@ var GameVfxThree = (() => {
|
|
|
7422
7466
|
metal_door: {
|
|
7423
7467
|
name: "Metal door denting",
|
|
7424
7468
|
short: "Each blow sinks the plate, flakes the paint and throws sparks; the hinge bursts.",
|
|
7425
|
-
cost: "\u2264 160
|
|
7469
|
+
cost: "\u2264 160 particles",
|
|
7426
7470
|
costNote: "deformable mesh",
|
|
7427
7471
|
when: "A door, gate or steel plate taking a zombie blow (Brute 3x), a 12-gauge shot or a car hit.",
|
|
7428
7472
|
look: "The plate sinks where it is hit (a deformed mesh, not a model swap), with 3 visible levels. Each blow gives off sparks, flakes of green paint and rust dust. When the health runs out the top hinge bursts in sparks, the door topples and scrapes on the ground giving off more sparks.",
|
|
@@ -7432,7 +7476,7 @@ var GameVfxThree = (() => {
|
|
|
7432
7476
|
iron_railing: {
|
|
7433
7477
|
name: "Iron railing bending",
|
|
7434
7478
|
short: "The horde pushes, the bars bow, the weld pops in a spark and the railing falls.",
|
|
7435
|
-
cost: "\u2264 200
|
|
7479
|
+
cost: "\u2264 200 particles",
|
|
7436
7480
|
costNote: "3 deformable bars",
|
|
7437
7481
|
when: "A railing, iron gate, handrail or chain-link fence with zombies pushing (horde pressure) or a car hitting it.",
|
|
7438
7482
|
look: "The bars bow gradually at the point of pressure and the vertical ones tilt with them. Rust falls in a thread during the strain. At two moments the weld pops with a crackle of sparks. At the end the middle post gives and the railing topples, leaving the bent iron on the ground.",
|
|
@@ -7442,7 +7486,7 @@ var GameVfxThree = (() => {
|
|
|
7442
7486
|
wooden_fence: {
|
|
7443
7487
|
name: "Wooden fence breaking",
|
|
7444
7488
|
short: "Planks snap one by one, chips and dust, and the section topples as the horde passes.",
|
|
7445
|
-
cost: "\u2264 150
|
|
7489
|
+
cost: "\u2264 150 particles",
|
|
7446
7490
|
costNote: "12 planks",
|
|
7447
7491
|
when: "A fence, yard gate or plank barricade taking a shot, blow or horde pressure.",
|
|
7448
7492
|
look: "Each plank snaps at a random point: the stump stays on the rail and the top piece flies off with chips and wood dust. The zombie appears through the gap. At the end the rail cracks and the whole section topples forward.",
|
|
@@ -7452,7 +7496,7 @@ var GameVfxThree = (() => {
|
|
|
7452
7496
|
hit_point_breakage: {
|
|
7453
7497
|
name: "Breakage by the point hit",
|
|
7454
7498
|
short: "A shot to the leg, a machete on the corner, a bat on the top and a 12-gauge up close: each weapon breaks the part it hit.",
|
|
7455
|
-
cost: "\u2264 120
|
|
7499
|
+
cost: "\u2264 120 particles",
|
|
7456
7500
|
costNote: "parts with their own health",
|
|
7457
7501
|
when: "Any destructible item. The damage goes to the part that was hit, not to the whole item.",
|
|
7458
7502
|
look: "A 9 mm shot to the front leg: the lower part of the leg breaks off and the table tips toward that corner. A machete on the corner of the top: the corner comes off whole in a straight cut, with chips in a line. A bat on the other corner: the top cracks and falls apart in pieces with dust. A 12-gauge up close on what is left: 9 grouped holes that stay in the wood.",
|
|
@@ -7462,7 +7506,7 @@ var GameVfxThree = (() => {
|
|
|
7462
7506
|
pressure_cylinders: {
|
|
7463
7507
|
name: "Extinguisher and oxygen cylinder",
|
|
7464
7508
|
short: "A shot to the extinguisher makes a white cloud and a spin; on oxygen the valve breaks and the cylinder becomes a rocket.",
|
|
7465
|
-
cost: "\u2264 250
|
|
7509
|
+
cost: "\u2264 250 particles",
|
|
7466
7510
|
costNote: "cloud entity",
|
|
7467
7511
|
paramLabel: "Cylinder",
|
|
7468
7512
|
when: "A shot, a strong blow or fire on an extinguisher, oxygen cylinder or welding cylinder (extinguisher and oxygen_cylinder keys of the map catalogue).",
|
|
@@ -7473,7 +7517,7 @@ var GameVfxThree = (() => {
|
|
|
7473
7517
|
building_fire: {
|
|
7474
7518
|
name: "Building on fire",
|
|
7475
7519
|
short: "Fire coming out of the windows, licking the facade, embers and smoke with the wind.",
|
|
7476
|
-
cost: "\u2264 600
|
|
7520
|
+
cost: "\u2264 600 particles",
|
|
7477
7521
|
costNote: "2 lights \xB7 reference",
|
|
7478
7522
|
when: "A building fire (molotov, explosion, vehicle). It lasts minutes and spreads to the neighbour.",
|
|
7479
7523
|
look: "Windows lit orange that flicker, tongues of fire coming out of the top of each window and climbing the facade, embers carried by the wind and a thick slanted column of smoke. The light paints the street.",
|
|
@@ -7514,7 +7558,7 @@ var GameVfxThree = (() => {
|
|
|
7514
7558
|
engine_smoke: {
|
|
7515
7559
|
name: "Engine smoke",
|
|
7516
7560
|
short: "White steam, grey smoke, then black with fire, depending on condition.",
|
|
7517
|
-
cost: "\u2264 120
|
|
7561
|
+
cost: "\u2264 120 particles",
|
|
7518
7562
|
costNote: "continuous",
|
|
7519
7563
|
paramLabel: "Condition",
|
|
7520
7564
|
when: "Whenever the engine is on or hot, from the part condition (0 to 100) in the fleet brief.",
|
|
@@ -7525,7 +7569,7 @@ var GameVfxThree = (() => {
|
|
|
7525
7569
|
fuel_leak: {
|
|
7526
7570
|
name: "Tank leak and fire",
|
|
7527
7571
|
short: "Dripping petrol, a pool, a spark and the fire running to the tank.",
|
|
7528
|
-
cost: "\u2264 400
|
|
7572
|
+
cost: "\u2264 400 particles",
|
|
7529
7573
|
costNote: "fire entity",
|
|
7530
7574
|
when: "A tank punctured by a shot or a crash. A spark, a shot or nearby fire lights the pool.",
|
|
7531
7575
|
look: "Drops of petrol with an oily sheen forming a growing pool. A shot on the metal throws a spark, the fire lights on the pool, runs to the car and, if nobody puts it out, the car explodes.",
|
|
@@ -7535,7 +7579,7 @@ var GameVfxThree = (() => {
|
|
|
7535
7579
|
car_explosion: {
|
|
7536
7580
|
name: "Car explosion",
|
|
7537
7581
|
short: "A fireball, the car jumping, the bonnet and doors flying, the shell burning.",
|
|
7538
|
-
cost: "\u2264 600
|
|
7582
|
+
cost: "\u2264 600 particles",
|
|
7539
7583
|
costNote: "rare event",
|
|
7540
7584
|
when: "A tank on fire for more than 4 s, or damage above the threshold on a car with fuel.",
|
|
7541
7585
|
look: "A large explosion (grenade scale 1.8), the car jumps and lands crooked, the bonnet, doors and a wheel fly off, the glass bursts into shards. The bodywork goes black and the shell burns with black smoke for minutes.",
|
|
@@ -7596,7 +7640,7 @@ var GameVfxThree = (() => {
|
|
|
7596
7640
|
bloater_blindness: {
|
|
7597
7641
|
name: "Bloater blindness",
|
|
7598
7642
|
short: "Green gas in the face: a blurred screen for 5 s and mucus on the lens.",
|
|
7599
|
-
cost: "post + 30
|
|
7643
|
+
cost: "post + 30 particles",
|
|
7600
7644
|
costNote: "server entity",
|
|
7601
7645
|
when: "A player inside the Bloater cloud (radius 4 m) when it spawns.",
|
|
7602
7646
|
look: "A green cloud bursts between the zombie and the camera, the screen goes green and blurred and clears in 5 s, with drops of green mucus on the lens that run down. A cough shakes the camera twice.",
|
|
@@ -7606,7 +7650,7 @@ var GameVfxThree = (() => {
|
|
|
7606
7650
|
campfire: {
|
|
7607
7651
|
name: "Campsite fire",
|
|
7608
7652
|
short: "A low flame, embers spiralling up and a flickering warm light.",
|
|
7609
|
-
cost: "\u2264 120
|
|
7653
|
+
cost: "\u2264 120 particles",
|
|
7610
7654
|
costNote: "1 fire light",
|
|
7611
7655
|
when: "A campfire lit by the group or by NPCs. It warms, lights and draws zombies from afar (light).",
|
|
7612
7656
|
look: "Small low flames, embers that rise turning and fade, thin smoke and a warm light that flickers and paints the survivor.",
|
|
@@ -7616,7 +7660,7 @@ var GameVfxThree = (() => {
|
|
|
7616
7660
|
cough_and_breath: {
|
|
7617
7661
|
name: "Cough, breath and flashlight",
|
|
7618
7662
|
short: "Breath vapour in the cold, the fever cough visible to others and the flashlight beam.",
|
|
7619
|
-
cost: "\u2264 40
|
|
7663
|
+
cost: "\u2264 40 particles",
|
|
7620
7664
|
costNote: "1 spot with shadow",
|
|
7621
7665
|
when: "A cold night: breath always. Infection fever: a cough every few seconds, audible at 10 m.",
|
|
7622
7666
|
look: "A short puff of vapour every 2.4 s. On a cough, three quick bigger puffs and the body jolts. The flashlight shows the beam in the fog and is the only light with a shadow.",
|
|
@@ -8566,6 +8610,81 @@ void main(){
|
|
|
8566
8610
|
};
|
|
8567
8611
|
}
|
|
8568
8612
|
|
|
8613
|
+
// src/mesh-layer.ts
|
|
8614
|
+
var SPHERE_DEFAULT_WIDTH = 8;
|
|
8615
|
+
var SPHERE_DEFAULT_HEIGHT = 6;
|
|
8616
|
+
function geometryOf(THREE, particle) {
|
|
8617
|
+
switch (particle.shape) {
|
|
8618
|
+
case "cone":
|
|
8619
|
+
return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, false);
|
|
8620
|
+
case "cone_open":
|
|
8621
|
+
return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, true);
|
|
8622
|
+
case "sphere":
|
|
8623
|
+
return new THREE.SphereGeometry(particle.radius, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
|
|
8624
|
+
case "icosahedron":
|
|
8625
|
+
return new THREE.IcosahedronGeometry(particle.radius, particle.segments);
|
|
8626
|
+
}
|
|
8627
|
+
}
|
|
8628
|
+
function materialOf(THREE, particle) {
|
|
8629
|
+
if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
|
|
8630
|
+
return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
|
|
8631
|
+
}
|
|
8632
|
+
function createMeshLayerProbes(THREE) {
|
|
8633
|
+
const geometry = new THREE.BoxGeometry(1e-3, 1e-3, 1e-3);
|
|
8634
|
+
return [{ lit: true, additive: false }, { lit: false, additive: false }, { lit: false, additive: true }].map(
|
|
8635
|
+
(kind) => new THREE.Mesh(geometry, materialOf(THREE, { ...kind, color: 8421504, opacity: 0.5 }))
|
|
8636
|
+
);
|
|
8637
|
+
}
|
|
8638
|
+
function createMeshLayer(THREE, pool) {
|
|
8639
|
+
const group = new THREE.Group();
|
|
8640
|
+
group.name = "workshop-meshes";
|
|
8641
|
+
const drawn = /* @__PURE__ */ new Map();
|
|
8642
|
+
const frame = new THREE.Quaternion();
|
|
8643
|
+
const euler = new THREE.Euler();
|
|
8644
|
+
const turn = new THREE.Quaternion();
|
|
8645
|
+
const offset = new THREE.Vector3();
|
|
8646
|
+
function release(mesh2) {
|
|
8647
|
+
mesh2.removeFromParent();
|
|
8648
|
+
mesh2.geometry.dispose();
|
|
8649
|
+
mesh2.material.dispose();
|
|
8650
|
+
}
|
|
8651
|
+
return {
|
|
8652
|
+
group,
|
|
8653
|
+
sync() {
|
|
8654
|
+
const live = /* @__PURE__ */ new Set();
|
|
8655
|
+
for (const particle of pool.particles) {
|
|
8656
|
+
live.add(particle.id);
|
|
8657
|
+
let mesh2 = drawn.get(particle.id);
|
|
8658
|
+
if (!mesh2) {
|
|
8659
|
+
mesh2 = new THREE.Mesh(geometryOf(THREE, particle), materialOf(THREE, particle));
|
|
8660
|
+
mesh2.frustumCulled = false;
|
|
8661
|
+
mesh2.renderOrder = particle.additive ? 5 : 3;
|
|
8662
|
+
drawn.set(particle.id, mesh2);
|
|
8663
|
+
group.add(mesh2);
|
|
8664
|
+
}
|
|
8665
|
+
frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
|
|
8666
|
+
offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
|
|
8667
|
+
mesh2.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
|
|
8668
|
+
turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
|
|
8669
|
+
mesh2.quaternion.copy(frame).multiply(turn);
|
|
8670
|
+
mesh2.scale.setScalar(particle.scale);
|
|
8671
|
+
mesh2.material.opacity = particle.opacity;
|
|
8672
|
+
}
|
|
8673
|
+
for (const [id, mesh2] of drawn) {
|
|
8674
|
+
if (live.has(id)) continue;
|
|
8675
|
+
release(mesh2);
|
|
8676
|
+
drawn.delete(id);
|
|
8677
|
+
}
|
|
8678
|
+
return pool.count;
|
|
8679
|
+
},
|
|
8680
|
+
dispose() {
|
|
8681
|
+
for (const mesh2 of drawn.values()) release(mesh2);
|
|
8682
|
+
drawn.clear();
|
|
8683
|
+
group.removeFromParent();
|
|
8684
|
+
}
|
|
8685
|
+
};
|
|
8686
|
+
}
|
|
8687
|
+
|
|
8569
8688
|
// src/screen-overlay.ts
|
|
8570
8689
|
var clamp5 = (value, min2, max2) => value < min2 ? min2 : value > max2 ? max2 : value;
|
|
8571
8690
|
function layer(container, name, blend) {
|
|
@@ -8745,75 +8864,6 @@ void main() {
|
|
|
8745
8864
|
};
|
|
8746
8865
|
}
|
|
8747
8866
|
|
|
8748
|
-
// src/mesh-layer.ts
|
|
8749
|
-
var SPHERE_DEFAULT_WIDTH = 8;
|
|
8750
|
-
var SPHERE_DEFAULT_HEIGHT = 6;
|
|
8751
|
-
function geometryOf(THREE, particle) {
|
|
8752
|
-
switch (particle.shape) {
|
|
8753
|
-
case "cone":
|
|
8754
|
-
return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, false);
|
|
8755
|
-
case "cone_open":
|
|
8756
|
-
return new THREE.ConeGeometry(particle.radius, particle.height, particle.segments || void 0, particle.segments2 || 1, true);
|
|
8757
|
-
case "sphere":
|
|
8758
|
-
return new THREE.SphereGeometry(particle.radius, particle.segments || SPHERE_DEFAULT_WIDTH, particle.segments2 || SPHERE_DEFAULT_HEIGHT);
|
|
8759
|
-
case "icosahedron":
|
|
8760
|
-
return new THREE.IcosahedronGeometry(particle.radius, particle.segments);
|
|
8761
|
-
}
|
|
8762
|
-
}
|
|
8763
|
-
function materialOf(THREE, particle) {
|
|
8764
|
-
if (particle.lit) return new THREE.MeshStandardMaterial({ color: particle.color, roughness: 1, metalness: 0, transparent: true, opacity: particle.opacity, depthWrite: false });
|
|
8765
|
-
return new THREE.MeshBasicMaterial({ color: particle.color, transparent: true, opacity: particle.opacity, depthWrite: false, ...particle.additive ? { blending: THREE.AdditiveBlending } : {} });
|
|
8766
|
-
}
|
|
8767
|
-
function createMeshLayer(THREE, pool) {
|
|
8768
|
-
const group = new THREE.Group();
|
|
8769
|
-
group.name = "workshop-meshes";
|
|
8770
|
-
const drawn = /* @__PURE__ */ new Map();
|
|
8771
|
-
const frame = new THREE.Quaternion();
|
|
8772
|
-
const euler = new THREE.Euler();
|
|
8773
|
-
const turn = new THREE.Quaternion();
|
|
8774
|
-
const offset = new THREE.Vector3();
|
|
8775
|
-
function release(mesh2) {
|
|
8776
|
-
mesh2.removeFromParent();
|
|
8777
|
-
mesh2.geometry.dispose();
|
|
8778
|
-
mesh2.material.dispose();
|
|
8779
|
-
}
|
|
8780
|
-
return {
|
|
8781
|
-
group,
|
|
8782
|
-
sync() {
|
|
8783
|
-
const live = /* @__PURE__ */ new Set();
|
|
8784
|
-
for (const particle of pool.particles) {
|
|
8785
|
-
live.add(particle.id);
|
|
8786
|
-
let mesh2 = drawn.get(particle.id);
|
|
8787
|
-
if (!mesh2) {
|
|
8788
|
-
mesh2 = new THREE.Mesh(geometryOf(THREE, particle), materialOf(THREE, particle));
|
|
8789
|
-
mesh2.frustumCulled = false;
|
|
8790
|
-
mesh2.renderOrder = particle.additive ? 5 : 3;
|
|
8791
|
-
drawn.set(particle.id, mesh2);
|
|
8792
|
-
group.add(mesh2);
|
|
8793
|
-
}
|
|
8794
|
-
frame.set(particle.frame[0], particle.frame[1], particle.frame[2], particle.frame[3]);
|
|
8795
|
-
offset.set(particle.at[0], particle.at[1], particle.at[2]).multiplyScalar(particle.scale).applyQuaternion(frame);
|
|
8796
|
-
mesh2.position.set(particle.x + offset.x, particle.y + offset.y, particle.z + offset.z);
|
|
8797
|
-
turn.setFromEuler(euler.set(particle.rx, particle.ry, particle.rz));
|
|
8798
|
-
mesh2.quaternion.copy(frame).multiply(turn);
|
|
8799
|
-
mesh2.scale.setScalar(particle.scale);
|
|
8800
|
-
mesh2.material.opacity = particle.opacity;
|
|
8801
|
-
}
|
|
8802
|
-
for (const [id, mesh2] of drawn) {
|
|
8803
|
-
if (live.has(id)) continue;
|
|
8804
|
-
release(mesh2);
|
|
8805
|
-
drawn.delete(id);
|
|
8806
|
-
}
|
|
8807
|
-
return pool.count;
|
|
8808
|
-
},
|
|
8809
|
-
dispose() {
|
|
8810
|
-
for (const mesh2 of drawn.values()) release(mesh2);
|
|
8811
|
-
drawn.clear();
|
|
8812
|
-
group.removeFromParent();
|
|
8813
|
-
}
|
|
8814
|
-
};
|
|
8815
|
-
}
|
|
8816
|
-
|
|
8817
8867
|
// src/shaders/workshop-sprite.ts
|
|
8818
8868
|
var WORKSHOP_SPRITE_VERTEX = `attribute float size;attribute float alpha;attribute vec3 pcol;varying vec3 vC;varying float vA;uniform float uScale;
|
|
8819
8869
|
void main(){vC=pcol;vA=alpha;vec4 mv=modelViewMatrix*vec4(position,1.);gl_PointSize=clamp(size*uScale/-mv.z,1.,512.);gl_Position=projectionMatrix*mv;}`;
|
|
@@ -9184,6 +9234,13 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9184
9234
|
const requested = options.vfx;
|
|
9185
9235
|
const world = isWorld(requested) ? requested : createVfx({ profile: options.quality ?? "medium", reducedMotion, ...requested });
|
|
9186
9236
|
const pools = world.pools();
|
|
9237
|
+
let anchors;
|
|
9238
|
+
const releaseAnchors = () => {
|
|
9239
|
+
if (!anchors) return;
|
|
9240
|
+
anchors.meshes[0]?.geometry.dispose();
|
|
9241
|
+
for (const mesh2 of anchors.meshes) mesh2.material.dispose();
|
|
9242
|
+
anchors = void 0;
|
|
9243
|
+
};
|
|
9187
9244
|
const root = new THREE.Group();
|
|
9188
9245
|
options.scene.add(root);
|
|
9189
9246
|
const direction = new THREE.Vector3(...options.lightDirection ?? LAB_LIGHT_DIRECTION).normalize();
|
|
@@ -9300,6 +9357,16 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9300
9357
|
syncAll();
|
|
9301
9358
|
overlay?.update(screenNow());
|
|
9302
9359
|
},
|
|
9360
|
+
warmUpProbes() {
|
|
9361
|
+
releaseAnchors();
|
|
9362
|
+
const meshes = createMeshLayerProbes(THREE);
|
|
9363
|
+
for (const mesh2 of meshes) mesh2.frustumCulled = false;
|
|
9364
|
+
const group = new THREE.Group();
|
|
9365
|
+
group.add(...meshes);
|
|
9366
|
+
anchors = { meshes, group };
|
|
9367
|
+
return { group, dispose: () => {
|
|
9368
|
+
} };
|
|
9369
|
+
},
|
|
9303
9370
|
applyCamera() {
|
|
9304
9371
|
const shake = screenNow().shake;
|
|
9305
9372
|
if (reducedMotion || !(shake > SHAKE_FLOOR)) return () => {
|
|
@@ -9318,6 +9385,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9318
9385
|
},
|
|
9319
9386
|
screen: screenNow,
|
|
9320
9387
|
dispose() {
|
|
9388
|
+
releaseAnchors();
|
|
9321
9389
|
stopMarks();
|
|
9322
9390
|
overlay?.dispose();
|
|
9323
9391
|
workshopLayers?.dispose();
|
|
@@ -9582,6 +9650,11 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9582
9650
|
controls,
|
|
9583
9651
|
layers,
|
|
9584
9652
|
metrics,
|
|
9653
|
+
// opening the effects area puts the camera at the bench view (the initial distance does not move it: that would take the player's view on load)
|
|
9654
|
+
area: () => ({
|
|
9655
|
+
placements: [],
|
|
9656
|
+
view: { eye: eyeFor(DEFAULT_DISTANCE), target: [0, 1.2, 0] }
|
|
9657
|
+
}),
|
|
9585
9658
|
attach(host) {
|
|
9586
9659
|
const bench = host.bench;
|
|
9587
9660
|
let own;
|
|
@@ -9601,6 +9674,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9601
9674
|
let phase = 0;
|
|
9602
9675
|
let handle = null;
|
|
9603
9676
|
let sinceRepeat = 0;
|
|
9677
|
+
let live = false;
|
|
9604
9678
|
read = () => {
|
|
9605
9679
|
const def = explosions[effect];
|
|
9606
9680
|
const recipe = recipes[effect];
|
|
@@ -9639,7 +9713,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9639
9713
|
}
|
|
9640
9714
|
);
|
|
9641
9715
|
bench?.sheet?.(sheetFor(id));
|
|
9642
|
-
replay();
|
|
9716
|
+
if (live) replay();
|
|
9643
9717
|
}
|
|
9644
9718
|
function look() {
|
|
9645
9719
|
const eye = eyeFor(distance);
|
|
@@ -9656,7 +9730,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9656
9730
|
choose(change.value);
|
|
9657
9731
|
else if (change.id === "variant" && typeof change.value === "string") {
|
|
9658
9732
|
variant = change.value;
|
|
9659
|
-
if (recipes[effect]?.params) replay();
|
|
9733
|
+
if (live && recipes[effect]?.params) replay();
|
|
9660
9734
|
} else if (change.id === "auto_repeat") auto = change.value === true;
|
|
9661
9735
|
else if (change.id === "distance" && typeof change.value === "number") {
|
|
9662
9736
|
distance = change.value;
|
|
@@ -9666,6 +9740,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
9666
9740
|
};
|
|
9667
9741
|
const offChange = bench?.onChange(apply);
|
|
9668
9742
|
const offFrame = host.onFrame((dt) => {
|
|
9743
|
+
live = true;
|
|
9669
9744
|
phase += dt;
|
|
9670
9745
|
sinceRepeat += dt;
|
|
9671
9746
|
own?.update(dt);
|