feat(effects): вода, облако яда, луч света; пульт и окна демонстрации

- Поле: вода (сплошная заливка по штриху, превью кистью), туман/огонь/дождь без изменений логики.

- Действия: облако яда (частицы, круглая текстура, звук oblako-yada.mp3, длительность как у трека), луч света и заморозка со звуками из public/.

- Пульт: инструменты воды и яда, синхрон SFX, тесты панели и ластика.

- Окно управления: дочернее от окна просмотра (Z-order).

- Типы эффектов, effectsStore prune, hit-test ластика.

Made-with: Cursor
This commit is contained in:
Ivan Fontosh
2026-04-20 11:03:57 +08:00
parent 726c89e104
commit 20c838da7d
19 changed files with 1154 additions and 111 deletions
+31
View File
@@ -20,6 +20,20 @@ void test('pickEraseTargetId: fire/rain по штриху как туман', ()
assert.equal(id, 'f1');
});
void test('pickEraseTargetId: вода по штриху как туман', () => {
const water: EffectInstance = {
...base,
id: 'w1',
type: 'water',
points: [{ x: 0.4, y: 0.55, tMs: 0 }],
radiusN: 0.06,
opacity: 0.5,
lifetimeMs: null,
};
const id = pickEraseTargetId([water], { x: 0.41, y: 0.55 }, 0.05);
assert.equal(id, 'w1');
});
void test('minDistSqEffectToPoint: молния — расстояние до отрезка', () => {
const bolt: EffectInstance = {
...base,
@@ -37,6 +51,23 @@ void test('minDistSqEffectToPoint: молния — расстояние до о
assert.equal(end, 0);
});
void test('minDistSqEffectToPoint: луч света — как у молнии, отрезок', () => {
const beam: EffectInstance = {
...base,
id: 'S1',
type: 'sunbeam',
start: { x: 0.5, y: 0 },
end: { x: 0.5, y: 0.8 },
widthN: 0.04,
intensity: 1,
lifetimeMs: 220,
};
const onBeam = minDistSqEffectToPoint(beam, { x: 0.5, y: 0.4 });
assert.equal(onBeam, 0);
const aside = minDistSqEffectToPoint(beam, { x: 0.52, y: 0.4 });
assert.ok(aside > 0 && aside < 0.01);
});
void test('pickEraseTargetId: scorch с учётом inst.radiusN', () => {
const sc: EffectInstance = {
...base,