feat(traps): activation VFX/SFX, explosion effect, and help section
Wire mimic/pit/arrow/laser media on activate, poison/explosion via effects, and document the scene editor and traps in Instructions. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -82,3 +82,22 @@ void test('pruneExpired: яд удаляется после lifetime', () => {
|
||||
assert.equal(store.pruneExpired(), true);
|
||||
assert.equal(store.getState().instances.length, 0);
|
||||
});
|
||||
|
||||
void test('pruneExpired: взрыв удаляется после lifetime', () => {
|
||||
const store = new EffectsStore();
|
||||
store.dispatch({
|
||||
kind: 'instance.add',
|
||||
instance: {
|
||||
id: 'ex_test',
|
||||
type: 'explosion',
|
||||
seed: 1,
|
||||
createdAtMs: Date.now() - 20_000,
|
||||
at: { x: 0.5, y: 0.5 },
|
||||
radiusN: 0.08,
|
||||
intensity: 1,
|
||||
lifetimeMs: 4200,
|
||||
},
|
||||
});
|
||||
assert.equal(store.pruneExpired(), true);
|
||||
assert.equal(store.getState().instances.length, 0);
|
||||
});
|
||||
|
||||
@@ -52,6 +52,7 @@ export class EffectsStore {
|
||||
i.type === 'lightning' ||
|
||||
i.type === 'sunbeam' ||
|
||||
i.type === 'poisonCloud' ||
|
||||
i.type === 'explosion' ||
|
||||
i.type === 'darkness'
|
||||
) {
|
||||
return now - i.createdAtMs < i.lifetimeMs;
|
||||
|
||||
@@ -377,6 +377,7 @@ async function main() {
|
||||
registerHandler(ipcChannels.license.acceptEula, ({ version }) => licenseService.acceptEula(version));
|
||||
registerHandler(ipcChannels.windows.openMultiWindow, () => {
|
||||
sceneDarknessStore.resetSession();
|
||||
sceneTrapsStore.resetSession();
|
||||
openMultiWindow();
|
||||
const project = projectStore.getOpenProject();
|
||||
if (project) {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import { SceneTrapsStore } from './sceneTrapsStore';
|
||||
|
||||
void test('SceneTrapsStore: сохраняет runtime при переключении сцен внутри сессии', () => {
|
||||
const store = new SceneTrapsStore();
|
||||
store.switchScene('scene_a', ['t1']);
|
||||
store.dispatch({ kind: 'activate', trapId: 't1' });
|
||||
assert.equal(store.getState().byId.t1?.status, 'active');
|
||||
assert.equal(store.getState().byId.t1?.revealed, true);
|
||||
|
||||
store.switchScene('scene_b', ['t2']);
|
||||
assert.equal(store.getState().byId.t2?.status, 'inactive');
|
||||
|
||||
store.switchScene('scene_a', ['t1']);
|
||||
assert.equal(store.getState().byId.t1?.status, 'active');
|
||||
assert.equal(store.getState().byId.t1?.revealed, true);
|
||||
});
|
||||
|
||||
void test('SceneTrapsStore: resetSession сбрасывает кэш к дефолту (кнопка «Запустить»)', () => {
|
||||
const store = new SceneTrapsStore();
|
||||
store.switchScene('scene_a', ['t1', 't2']);
|
||||
store.dispatch({ kind: 'reveal', trapId: 't1' });
|
||||
store.dispatch({ kind: 'activate', trapId: 't2' });
|
||||
assert.equal(store.getState().byId.t1?.revealed, true);
|
||||
assert.equal(store.getState().byId.t2?.status, 'active');
|
||||
assert.ok(store.getState().lastActivation);
|
||||
|
||||
store.resetSession();
|
||||
store.switchScene('scene_a', ['t1', 't2']);
|
||||
|
||||
assert.equal(store.getState().byId.t1?.status, 'inactive');
|
||||
assert.equal(store.getState().byId.t1?.revealed, false);
|
||||
assert.equal(store.getState().byId.t2?.status, 'inactive');
|
||||
assert.equal(store.getState().byId.t2?.revealed, false);
|
||||
assert.equal(store.getState().lastActivation, null);
|
||||
});
|
||||
Reference in New Issue
Block a user