feat(effects): extinguish fire with water/rain and ambient SFX volume

Water and rain erase fire under a tight brush hit radius; add looping fire/rain ambients and an Effects sound slider that also scales one-shot effect SFX.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ivan Fontosh
2026-07-23 13:12:41 +08:00
parent a797162f16
commit 2c0e6fbf09
16 changed files with 370 additions and 16 deletions
+66
View File
@@ -123,3 +123,69 @@ void test('applyFieldEffectEraserStroke: пустой штрих — без из
assert.equal(next.length, 1);
assert.equal(next[0]?.id, 'w1');
});
void test('applyFieldEffectEraserStroke: types=["fire"] гасит только огонь', () => {
const fire: EffectInstance = {
...base,
id: 'fire1',
type: 'fire',
points: [
{ x: 0.5, y: 0.5, tMs: 0 },
{ x: 0.55, y: 0.5, tMs: 50 },
],
radiusN: 0.03,
opacity: 1,
lifetimeMs: null,
};
const fog: EffectInstance = {
...base,
id: 'fog1',
type: 'fog',
points: [{ x: 0.5, y: 0.5, tMs: 0 }],
radiusN: 0.03,
opacity: 0.5,
lifetimeMs: null,
};
const next = applyFieldEffectEraserStroke(
[fire, fog],
[{ x: 0.5, y: 0.5, tMs: 0 }],
0.08,
makeId,
['fire'],
);
assert.equal(next.length, 1);
assert.equal(next[0]?.id, 'fog1');
});
void test('applyFieldEffectEraserStroke: hitMode=brush не гасит издалека', () => {
const fire: EffectInstance = {
...base,
id: 'fire1',
type: 'fire',
points: [{ x: 0.5, y: 0.5, tMs: 0 }],
radiusN: 0.08,
opacity: 1,
lifetimeMs: null,
};
// brush ≈ 0.38 * 0.08 ≈ 0.030; dist 0.06 — мимо, dist 0.02 — попадание.
const far = applyFieldEffectEraserStroke(
[fire],
[{ x: 0.5, y: 0.56, tMs: 0 }],
0.08,
makeId,
['fire'],
'brush',
);
assert.equal(far.length, 1);
assert.equal(far[0]?.id, 'fire1');
const near = applyFieldEffectEraserStroke(
[fire],
[{ x: 0.5, y: 0.52, tMs: 0 }],
0.08,
makeId,
['fire'],
'brush',
);
assert.equal(near.length, 0);
});