Files
leggenda-del-regolo/tests/multimedia.test.mjs
T

103 lines
5.3 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { Game } from '../src/engine/game.js';
import { story } from '../src/story/story.js';
import { settle } from './helpers.mjs';
import { Soundscape } from '../src/engine/soundscape.js';
import { soundscapes, soundscapeFor } from '../src/story/soundscapes.js';
function finale() {
const g = new Game(story, { state: { scene: 'fortezza', x: 462, y: 310, facing: 1, inventory: ['torcia'],
flags: { indizio_regolo: true, casetta_aperta: true }, used: {}, ended: false } });
g.act('use', 'fessura', 'torcia'); settle(g);
return g;
}
test('la torcia aspetta la ricerca, richiede una sosta e non scopre gli occhi prima del quadro', () => {
const g = finale();
assert.equal(g.closeup.step, 'search');
assert.equal(g.act('look', 'bastione'), false);
assert.equal(g.walkTo([100, 300]), false);
assert.equal(g.travel('piazza'), false);
const eyes = story.closeups.fessura.targets[2].at;
g.closeupAim(eyes); g.update(2);
assert.equal(g.closeup.found, 0);
g.closeupAim(story.closeups.fessura.targets[0].at); g.update(0.3);
g.closeupAim(eyes); g.update(0.2);
g.closeupAim(story.closeups.fessura.targets[0].at); g.update(0.3);
assert.equal(g.closeup.step, 'search', 'la sosta riparte quando si sposta il fascio');
g.update(0.2);
assert.equal(g.closeup.step, 'reveal');
assert.equal(g.speech, null, 'la pietra non rivela il testo del quadro');
assert.equal(g.state.flags.palio_trovato, undefined);
const lines = settle(g, { explore: true }).map(s => s.text);
assert.ok(lines.indexOf('È il palio! Il quadro dei ragazzi delle medie!') < lines.indexOf('E quelli... sono due occhi gialli!'));
assert.ok(g.checkpoint.ended && g.checkpoint.flags.palio_trovato);
});
test('uscire dalla fessura o ricaricare durante la ricerca non finisce la partita e permette di riprovare', () => {
const g = finale(), saved = g.savable();
g.closeupAim(story.closeups.fessura.targets[0].at); g.update(1);
assert.deepEqual(g.savable(), saved);
assert.ok(g.closeupClose()); settle(g);
assert.ok(!g.state.ended && !g.state.flags.palio_trovato);
assert.ok(g.state.inventory.includes('torcia'));
const reload = new Game(story, { state: g.savable() });
reload.act('use', 'fessura', 'torcia'); settle(reload);
assert.equal(reload.closeup.found, 0);
settle(reload, { explore: true });
assert.ok(reload.state.ended);
});
test('aprire un primo piano ferma gli effetti successivi fino alla sua chiusura', () => {
const g = new Game(story);
g.queue = [{ closeup: 'libro' }, { set: 'dopo_libro' }];
g.update(0);
assert.equal(g.state.flags.dopo_libro, undefined);
g.closeupClose(); settle(g);
assert.equal(g.state.flags.dopo_libro, true);
});
function audioContext() {
const param = () => ({ value: 0, setValueAtTime(v) { this.value = v; }, linearRampToValueAtTime(v) { this.value = v; },
exponentialRampToValueAtTime(v) { this.value = v; }, setTargetAtTime(v) { this.value = v; }, cancelScheduledValues() {} });
const node = () => ({ gain: param(), frequency: param(), Q: param(), pan: param(), connect() {}, disconnect() {}, start() {}, stop() {} });
return { currentTime: 0, sampleRate: 100, destination: {}, state: 'running',
createGain: node, createBufferSource: node, createBiquadFilter: node, createStereoPanner: node, createOscillator: node,
createBuffer: (_, length) => ({ getChannelData: () => new Float32Array(length) }) };
}
test('suoni: muto e pausa fermano anche gli eventi già avviati, poi il fondo riparte', () => {
const s = new Soundscape({ createContext: audioContext, random: () => 0.3 });
s.unlock(); s.configure({ active: true }); s.update('acqua', 'pomeriggio', 0.1); s.play('page');
assert.ok(s.nodes.size > 1);
s.configure({ muted: true });
assert.equal(s.nodes.size, 0); assert.equal(s.master.gain.value, 0);
s.play('bell'); assert.equal(s.nodes.size, 0);
s.configure({ muted: false }); s.update('acqua', 'pomeriggio', 0.1);
assert.ok(s.bed);
s.configure({ active: false }); assert.equal(s.nodes.size, 0);
s.configure({ active: true, volume: 0 }); s.play('page'); assert.equal(s.nodes.size, 0);
});
test('suoni: profili distinti, niente uccelli di sera, nessun accumulo ai cambi di luogo', () => {
assert.equal(soundscapeFor('fortezza', 'fessura'), 'fessura');
assert.notEqual(soundscapeFor('campomigliaio_ponte'), soundscapeFor('pieve_interno'));
for (const def of Object.values(soundscapes)) assert.ok(!def.night.includes('bird'));
const s = new Soundscape({ createContext: audioContext, random: () => 0.3 });
s.unlock(); s.configure({ active: true });
for (let i = 0; i < 30; i++) {
s.update(i % 2 ? 'acqua' : 'bosco', 'sera', 0.1);
assert.equal(s.nodes.size, 1);
}
const events = []; s.play = kind => events.push(kind);
s.update('paese', 'sera', 0.1); s.update('paese', 'sera', 10);
assert.deepEqual(events, ['cricket']);
s.update('pieve', 'sera', 0.1); assert.equal(s.nodes.size, 0);
});
test('Web Audio mancante non interrompe la partita', () => {
const s = new Soundscape({ createContext: () => { throw new Error('non disponibile'); } });
assert.doesNotThrow(() => { s.unlock(); s.configure({ active: true }); s.update('paese', 'sera', 1); s.play('page'); });
});