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
@@ -177,6 +177,30 @@ void test('ControlApp: весь контент скроллится в окне,
assert.doesNotMatch(css, /\.rightStack\s*\{[^}]*overflow-y:\s*auto/s);
});
void test('ControlApp: вода/дождь гасят огонь; ambient огня/дождя + слайдер «Звук эффектов»', () => {
const src = readControlApp();
const fireSfx = fs.readFileSync(path.join(here, 'fireAmbientSfx.ts'), 'utf8');
const rainSfx = fs.readFileSync(path.join(here, 'rainAmbientSfx.ts'), 'utf8');
const i18n = fs.readFileSync(path.join(here, '../editor/i18n/editorMessages.ts'), 'utf8');
assert.ok(src.includes('extinguishFireAlong'));
assert.ok(src.includes("types: ['fire']"));
assert.ok(src.includes("hitMode: 'brush'"));
assert.ok(src.includes('effectsSoundRow'));
assert.ok(src.includes('setFireAmbientActive'));
assert.ok(src.includes('setRainAmbientActive'));
assert.ok(src.includes('hasFireOnScene'));
assert.ok(src.includes('hasRainOnScene'));
assert.ok(src.includes("t('control.effectsSound')"));
assert.ok(fireSfx.includes('fire-ambient.mp3'));
assert.ok(rainSfx.includes('rain-ambient.mp3'));
assert.ok(i18n.includes("'control.effectsSound': 'Звук эффектов'"));
const radius = src.indexOf("t('control.brushRadius')");
const effectsSound = src.indexOf("t('control.effectsSound')");
assert.ok(radius !== -1 && effectsSound !== -1 && radius < effectsSound);
});
void test('ControlApp: загрузка камп. аудио — useEffect зависит только от api и campaignAudioSpecKey', () => {
const src = readControlApp();
const re = /\/\/ Campaign elements:[\s\S]*?useEffect\(\(\) => \{[\s\S]*?\}\s*,\s*\[([^\]]*)\]\s*\)\s*;/;