Il Regolo del futuro (branch di Codex)
This commit is contained in:
+83
-19
@@ -131,28 +131,92 @@ try {
|
||||
await page.screenshot({ path: `${out}/${name}-super.png` });
|
||||
const multi = await page.evaluate(async () => {
|
||||
window.pinballShot('super');
|
||||
const m = await import('/src/engine/flipper.js'); m.update(game.closeup, 0.25); m.update(game.closeup, 0.25);
|
||||
// Ferma solo la simulazione per fotografare le tre palline, prima che una possa scendere.
|
||||
window.pinballResume = game.update.bind(game); game.update = dt => { game.closeup.visual.time += dt; };
|
||||
return { balls: m.activeBalls(game.closeup).length, locks: game.closeup.locks.length, reserve: game.closeup.balls, time: game.closeup.visual.time };
|
||||
const m = await import('/src/engine/flipper.js'), c = game.closeup;
|
||||
window.pinballResume = game.update.bind(game); game.update = () => {};
|
||||
window.pinballClassic = structuredClone(c); window.pinballClassic.transform = null;
|
||||
(await import('/src/engine/render.js')).render(document.querySelector('#scene').getContext('2d'), game, { time: performance.now() });
|
||||
m.update(c, 0.25); m.update(c, 0.2);
|
||||
window.pinballExplosion = structuredClone(c);
|
||||
return { balls: m.activeBalls(c).length, locks: c.locks.length, reserve: c.balls };
|
||||
});
|
||||
assert.deepEqual({ balls: multi.balls, locks: multi.locks, reserve: multi.reserve }, { balls: 3, locks: 0, reserve: 5 });
|
||||
await page.waitForFunction(t => game.closeup.visual.time > t + 0.7, multi.time);
|
||||
assert.deepEqual(multi, { balls: 3, locks: 0, reserve: 5 });
|
||||
await page.screenshot({ path: `${out}/${name}-esplosione.png` });
|
||||
await page.evaluate(async () => {
|
||||
const m = await import('/src/engine/flipper.js');
|
||||
for (let i = 0; i < 6; i++) m.update(game.closeup, 0.25);
|
||||
});
|
||||
await page.screenshot({ path: `${out}/${name}-trasformazione.png` });
|
||||
await page.evaluate(async () => {
|
||||
const m = await import('/src/engine/flipper.js');
|
||||
for (let i = 0; i < 6; i++) m.update(game.closeup, 0.25);
|
||||
window.pinballFuture = structuredClone(game.closeup);
|
||||
});
|
||||
assert.equal(await page.evaluate(() => game.closeup.table), 'future');
|
||||
await page.screenshot({ path: `${out}/${name}-futuro.png` });
|
||||
await page.screenshot({ path: `${out}/${name}-multiball.png` });
|
||||
// L'arretrato grafico (vedi la memoria «effetti leggeri»): 150 fotogrammi disegnati di fila con tre palline e gli
|
||||
// effetti vivi, poi una lettura dei pixel che aspetta la grafica. Il flipper di Codex ne lasciava 4,2 s; deve
|
||||
// restare sotto il secondo e mezzo anche col rendering software di queste prove.
|
||||
const backlog = await page.evaluate(async () => {
|
||||
const m = await import('/src/engine/flipper.js'), r = await import('/src/engine/render.js'), c = game.closeup;
|
||||
const ctx = document.querySelector('#scene').getContext('2d');
|
||||
ctx.getImageData(0, 0, 4, 4); // si aspetta prima l'arretrato di tutta la prova fin qui
|
||||
for (let i = 0; i < 150; i++) { m.update(c, 1 / 60); r.render(ctx, game, { time: performance.now() }); }
|
||||
const t = performance.now(); ctx.getImageData(0, 0, 4, 4); return Math.round(performance.now() - t);
|
||||
// Anche nel nuovo campo: due dita indipendenti, i due Shift e la pausa vera dell'applicazione.
|
||||
if (mobile) {
|
||||
await touch('touchStart', [P(250, 200), P(400, 200)]);
|
||||
await page.waitForFunction(() => game.closeup.flippers.left.held.length && game.closeup.flippers.right.held.length);
|
||||
await touch('touchEnd', []);
|
||||
} else {
|
||||
await page.keyboard.down('ShiftLeft'); await page.keyboard.down('ShiftRight');
|
||||
await page.waitForFunction(() => game.closeup.flippers.left.held.length && game.closeup.flippers.right.held.length);
|
||||
}
|
||||
await page.evaluate(() => { game.update = window.pinballResume; });
|
||||
await page.click('#pause-button');
|
||||
const paused = await page.evaluate(() => ({ sim: game.closeup.sim, hits: game.closeup.future.hits, score: game.closeup.score }));
|
||||
await page.waitForTimeout(250);
|
||||
assert.deepEqual(await page.evaluate(() => ({ sim: game.closeup.sim, hits: game.closeup.future.hits, score: game.closeup.score })), paused);
|
||||
assert.ok(await page.evaluate(() => !game.closeup.flippers.left.held.length && !game.closeup.flippers.right.held.length));
|
||||
if (!mobile) { await page.keyboard.up('ShiftLeft'); await page.keyboard.up('ShiftRight'); }
|
||||
await page.click('#resume');
|
||||
await page.evaluate(() => { game.update = () => {}; Object.assign(game.closeup, structuredClone(window.pinballFuture)); });
|
||||
// Stessa lettura sincrona dei pixel per classico, detonazione/trasformazione e futuro. Si svuota prima
|
||||
// l'arretrato precedente; in gioco si riportano in alto solo le palline prossime alla buca per misurare
|
||||
// tutti i 150 fotogrammi con tre palline, senza finire accidentalmente sul campo classico.
|
||||
for (const mode of ['classico', 'esplosione', 'futuro']) {
|
||||
const backlog = await page.evaluate(async mode => {
|
||||
const m = await import('/src/engine/flipper.js'), r = await import('/src/engine/render.js'), c = game.closeup;
|
||||
Object.assign(c, structuredClone(mode === 'classico' ? window.pinballClassic : mode === 'esplosione' ? window.pinballExplosion : window.pinballFuture));
|
||||
const ctx = document.querySelector('#scene').getContext('2d');
|
||||
r.render(ctx, game, { time: performance.now() }); ctx.getImageData(0, 0, 4, 4);
|
||||
for (let i = 0; i < 150; i++) {
|
||||
if (mode !== 'esplosione') for (const [k, b] of m.activeBalls(c).entries()) {
|
||||
b.releaseAt = 0;
|
||||
if (b.y > 295) Object.assign(b, { x: 260 + k * 55, y: 200, vx: 20, vy: -150, ramp: null });
|
||||
}
|
||||
m.update(c, 1 / 60); r.render(ctx, game, { time: performance.now() });
|
||||
}
|
||||
const t = performance.now(); ctx.getImageData(0, 0, 4, 4); const ms = Math.round(performance.now() - t);
|
||||
if (mode === 'futuro' && c.table !== 'future') throw new Error('misura uscita dal futuro');
|
||||
return ms;
|
||||
}, mode);
|
||||
console.log(`${name}: arretrato grafico ${mode} dopo 150 fotogrammi ${backlog} ms`);
|
||||
assert.ok(backlog < 1500, `${mode}: arretrato grafico ${backlog} ms`);
|
||||
}
|
||||
// Percorsi veri fino al finale: la schermata mostra sigilli caricati e il bersaglio mobile da colpire.
|
||||
await page.evaluate(async () => {
|
||||
const m = await import('/src/engine/flipper.js'), c = game.closeup;
|
||||
Object.assign(c, structuredClone(window.pinballFuture));
|
||||
for (const b of c.extraBalls) b.releaseAt = c.sim + 1000;
|
||||
for (const id of ['orbita', 'ponte', 'spira']) for (let i = 0; i < 2; i++) window.pinballShot(id);
|
||||
});
|
||||
console.log(`${name}: arretrato grafico dopo 150 fotogrammi ${backlog} ms`);
|
||||
assert.ok(backlog < 1500, `arretrato grafico ${backlog} ms`);
|
||||
await page.waitForFunction(() => document.querySelector('#scene').style.backgroundImage.startsWith('url('), null, { timeout: 2000 }); // il mobile sullo sfondo CSS
|
||||
await page.evaluate(() => { game.update = window.pinballResume; delete window.pinballResume; delete window.pinballShot; });
|
||||
assert.deepEqual(await page.evaluate(() => game.closeup.future.hits), { orbita: 2, ponte: 2, spira: 2 });
|
||||
await page.screenshot({ path: `${out}/${name}-sigilli.png` });
|
||||
await page.evaluate(async () => {
|
||||
const m = await import('/src/engine/flipper.js'), c = game.closeup;
|
||||
Object.assign(c.ball, { x: 320 + Math.sin((c.sim + m.STEP) * 1.6) * 24, y: c.regolo.y + c.regolo.r + 4, vx: 0, vy: -250, ramp: null, contacts: [] });
|
||||
m.update(c, m.STEP);
|
||||
});
|
||||
assert.equal(await page.evaluate(() => game.closeup.table), 'classic');
|
||||
await page.evaluate(async () => {
|
||||
(await import('/src/engine/render.js')).render(document.querySelector('#scene').getContext('2d'), game, { time: performance.now() });
|
||||
const m = await import('/src/engine/flipper.js'); m.update(game.closeup, 0.25); m.update(game.closeup, 0.25);
|
||||
});
|
||||
await page.screenshot({ path: `${out}/${name}-ritorno.png` });
|
||||
await page.waitForFunction(() => document.querySelector('#scene').style.backgroundImage.startsWith('url('));
|
||||
await page.evaluate(() => { game.update = window.pinballResume; for (const key of ['pinballResume', 'pinballShot', 'pinballClassic', 'pinballExplosion', 'pinballFuture']) delete window[key]; });
|
||||
// Si consumano le palline rimaste, compresa quella vinta, per arrivare al risultato reale.
|
||||
const finalScore = await page.evaluate(async () => {
|
||||
const m = await import('/src/engine/flipper.js'), c = game.closeup;
|
||||
|
||||
+128
-18
@@ -316,13 +316,16 @@ test('due LOCK aprono la SUPER SPECIAL: jackpot, pallina premio e multiball real
|
||||
assert.equal(m.activeBalls(c).length, 3); assert.ok(m.multiball(c));
|
||||
assert.equal(new Set(m.activeBalls(c)).size, 3, 'tre palline distinte');
|
||||
assert.ok(c.events.some(e => e.kind === 'flipper-super'));
|
||||
const winScore = c.score, lives = c.balls;
|
||||
rampShot(c, ids[0]);
|
||||
assert.ok(c.score - winScore >= m.SCORE.ramp + m.SCORE.jackpot, 'in multiball le rampe pagano jackpot');
|
||||
assert.ok(c.transform, 'la SUPER avvia la trasformazione');
|
||||
run(c, m.FUTURE.duration);
|
||||
assert.equal(c.table, 'future');
|
||||
const lives = c.balls;
|
||||
for (const b of m.activeBalls(c)) b.releaseAt = 0;
|
||||
// Metti tutte le palline sotto il campo nello stesso passo: la vita si scala una volta soltanto.
|
||||
for (const ball of m.activeBalls(c)) Object.assign(ball, { x: 320, y: 348, vx: 0, vy: 300, ramp: null, waiting: false, releaseAt: 0 });
|
||||
c.saveUntil = 0; m.update(c, m.STEP);
|
||||
assert.equal(c.balls, lives - 1); assert.equal(c.extraBalls.length, 0); assert.ok(m.waiting(c));
|
||||
assert.equal(c.table, 'classic', 'persa l’ultima si torna al River');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -338,25 +341,28 @@ test('la finestra LOCK scade in simulazione, cambiare rampa interrompe la serie
|
||||
assert.equal(c.rampRun.count, 0);
|
||||
});
|
||||
|
||||
test('la SUPER accompagna alla paletta sinistra e il multiball libera le palline sfalsate', () => {
|
||||
test('la trasformazione protegge le tre palline, poi le libera sfalsate verso le palette', () => {
|
||||
for (const level of Object.keys(m.LEVELS)) {
|
||||
const c = m.newFlipper(77, { level }); doubleLock(c); rampShot(c, 'super');
|
||||
const main = c.ball, delayed = c.extraBalls.find(b => b.releaseAt > c.sim);
|
||||
assert.equal(main.x, 229); assert.equal(main.y, 220); assert.equal(main.vx, 0); assert.equal(main.vy, 160);
|
||||
for (const b of c.extraBalls) { assert.ok([229, 411].includes(b.x)); assert.equal(b.vx, 0, 'i lock non sparano verso le buche laterali'); }
|
||||
assert.ok(delayed, 'una serratura aspetta prima di liberare la terza pallina');
|
||||
const position = [delayed.x, delayed.y]; run(c, 0.2);
|
||||
assert.deepEqual([delayed.x, delayed.y], position, 'durante il ritardo resta nel lock');
|
||||
run(c, 0.3); assert.notDeepEqual([delayed.x, delayed.y], position, 'poi viene liberata');
|
||||
// Isola la pallina della SUPER per verificare che un colpo normale possa recuperarla.
|
||||
c.extraBalls = []; c.ball = main;
|
||||
const before = m.activeBalls(c).map(b => [b.x, b.y]), score = c.score, lives = c.balls;
|
||||
run(c, 0.8); assert.deepEqual(m.activeBalls(c).map(b => [b.x, b.y]), before);
|
||||
assert.equal(c.score, score); assert.equal(c.balls, lives);
|
||||
run(c, m.FUTURE.duration - 0.8);
|
||||
assert.equal(c.table, 'future'); assert.equal(c.transform, null);
|
||||
const main = c.ball, delayed = c.extraBalls.at(-1), position = [delayed.x, delayed.y];
|
||||
assert.ok(delayed.releaseAt > c.sim);
|
||||
run(c, 0.2); assert.deepEqual([delayed.x, delayed.y], position);
|
||||
// Isola la prima pallina per verificare il recupero dalla paletta, senza alterare la geometria.
|
||||
const d = structuredClone(c); d.extraBalls = [];
|
||||
let recovered = false;
|
||||
for (let i = 0; i < 480; i++) {
|
||||
if (main.x > 284 && main.y > 300) m.hold(c, 'left', 'test', true);
|
||||
m.update(c, m.STEP);
|
||||
if (main.vy < -250) { recovered = true; break; }
|
||||
if (d.ball.x > 284 && d.ball.y > 300) m.hold(d, 'left', 'test', true);
|
||||
m.update(d, m.STEP);
|
||||
if (d.ball.vy < -250) { recovered = true; break; }
|
||||
}
|
||||
assert.ok(recovered, `${level}: ripresa dalla paletta, senza cadere direttamente nella buca centrale`);
|
||||
assert.ok(recovered, `${level}: prima pallina ripresa dalla paletta`);
|
||||
run(c, 1); assert.notDeepEqual([delayed.x, delayed.y], position);
|
||||
assert.ok(Number.isFinite(main.x));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -373,7 +379,8 @@ test('perdere una sostitutiva restituisce un lock alla volta senza consumare vit
|
||||
});
|
||||
|
||||
test('il multiball ha contatti indipendenti, urti fra palline e palette che avanzano una sola volta', () => {
|
||||
const c = m.newFlipper(24); doubleLock(c); rampShot(c, 'super');
|
||||
const c = m.newFlipper(24); doubleLock(c); rampShot(c, 'super'); run(c, m.FUTURE.duration);
|
||||
for (const b of m.activeBalls(c)) b.releaseAt = 0;
|
||||
const single = m.newFlipper(24); m.hold(single, 'left', 'test', true); m.hold(c, 'left', 'test', true);
|
||||
m.update(single, m.STEP); m.update(c, m.STEP);
|
||||
assert.ok(Math.abs(c.flippers.left.angle - single.flippers.left.angle) < 1e-10);
|
||||
@@ -473,3 +480,106 @@ test('dietro la Pieve non c\'è più il canale che portava dritto all\'uscita si
|
||||
assert.ok(!inChannel, 'mai fra la parete e il dorso dei bersagli');
|
||||
assert.ok(c.ball.x > 210 || c.ball.y < 146, `in campo (${Math.round(c.ball.x)}, ${Math.round(c.ball.y)})`);
|
||||
});
|
||||
|
||||
function inFuture(level = 'seria') {
|
||||
const c = m.newFlipper(96, { level }); doubleLock(c); rampShot(c, 'super'); run(c, m.FUTURE.duration);
|
||||
// Gli altri tiri restano parcheggiati mentre si misura un percorso alla volta.
|
||||
for (const b of c.extraBalls) b.releaseAt = c.sim + 1000;
|
||||
c.ball.releaseAt = 0; return c;
|
||||
}
|
||||
|
||||
test('il futuro è un altro campo: tre giri reali, quote, punti e sigilli solo a percorso completo', () => {
|
||||
for (const level of Object.keys(m.LEVELS)) {
|
||||
const c = inFuture(level), old = m.layout();
|
||||
assert.notDeepEqual(c.walls, old.walls); assert.notDeepEqual(c.bumpers, old.bumpers);
|
||||
assert.equal(c.targets.length, 0); assert.equal(c.lanes.length, 0);
|
||||
for (const w of c.walls) for (const [x, y] of [w.a, w.b]) assert.ok(x >= 200 && x <= 448 && y >= 5 && y <= 336);
|
||||
for (const r of c.ramps) {
|
||||
const score = c.score;
|
||||
for (let n = 1; n <= 3; n++) {
|
||||
const result = rampShot(c, r.id);
|
||||
assert.ok(result.frames > 50); assert.ok(result.top < r.mouth[0][1] - 40);
|
||||
assert.equal(c.score - score, r.points * n); assert.equal(c.future.hits[r.id], Math.min(n, 2));
|
||||
assert.ok(result.exit.x === 229 || result.exit.x === 411, 'ritorno nelle corsie interne');
|
||||
}
|
||||
// Un ingresso debole sulla salita ricade e non carica il sigillo.
|
||||
const d = inFuture(level), rd = d.ramps.find(k => k.id === r.id), points = d.score;
|
||||
const result = ride(d, { x: rd.path[0][0], y: rd.mouth[0][1] + 1, vx: 0, vy: -270, ramp: null });
|
||||
assert.equal(d.future.hits[r.id], 0); assert.equal(d.score, points); assert.ok(result.exit.y >= rd.mouth[0][1]);
|
||||
}
|
||||
const score = c.score, lives = c.balls;
|
||||
const nextX = 320 + Math.sin((c.sim + m.STEP) * 1.6) * 24;
|
||||
put(c, { x: nextX, y: c.regolo.y + c.regolo.r + 4, vx: 0, vy: -250, ramp: null, contacts: [] });
|
||||
m.update(c, m.STEP);
|
||||
assert.equal(c.table, 'classic'); assert.equal(c.score, score + m.FUTURE.finale);
|
||||
assert.equal(c.balls, lives); assert.ok(m.waiting(c)); assert.equal(c.extraBalls.length, 0);
|
||||
assert.deepEqual(c.ramps.map(r => r.id), ['sinistra', 'destra', 'super']);
|
||||
doubleLock(c); rampShot(c, 'super'); assert.ok(c.transform, 'il viaggio è ripetibile');
|
||||
}
|
||||
});
|
||||
|
||||
test('nel futuro il Regolo si muove, gli urti e la molla funzionano, niente finale prima dei sigilli', () => {
|
||||
const c = inFuture(), x = c.regolo.x;
|
||||
c.ball.waiting = true; run(c, 0.5); assert.notEqual(c.regolo.x, x);
|
||||
assert.ok(m.key(c, ' ', true)); run(c, 0.4); m.key(c, ' ', false); assert.ok(!m.waiting(c));
|
||||
// Un respingente devia davvero la pallina.
|
||||
const b = c.bumpers[0], score = c.score;
|
||||
put(c, { x: b.x, y: b.y + b.r + 4, vx: 0, vy: -200, contacts: [] }); m.update(c, m.STEP);
|
||||
assert.ok(c.ball.vy > 0); assert.equal(c.score, score + m.SCORE.bumper);
|
||||
put(c, { x: c.regolo.x, y: c.regolo.y + c.regolo.r + 4, vx: 0, vy: -200, contacts: [] }); m.update(c, m.STEP);
|
||||
assert.equal(c.table, 'future'); assert.equal(c.score, score + m.SCORE.bumper + m.SCORE.regolo);
|
||||
});
|
||||
|
||||
test('futuro e trasformazione sono serializzabili; annullare e disegnare non fanno avanzare la partita', () => {
|
||||
for (const transforming of [false, true]) {
|
||||
const c = inFuture(); if (transforming) { doubleLockAfterReturn(c); }
|
||||
m.hold(c, 'left', 'touch', true); m.cancel(c);
|
||||
const saved = JSON.parse(JSON.stringify(c));
|
||||
const calls = [], gradient = { addColorStop() {} };
|
||||
const ctx = new Proxy({}, { get: (o, k) => k in o ? o[k] : (...args) => { calls.push([k, ...args]); return gradient; }, set: (o, k, v) => { o[k] = v; return true; } });
|
||||
for (let i = 0; i < 10; i++) drawFlipper(ctx, c, story.closeups.flipper, {});
|
||||
assert.deepEqual(JSON.parse(JSON.stringify(c)), saved, 'disegno e pausa non consumano tempo');
|
||||
assert.ok(!c.flippers.left.held.length);
|
||||
run(c, 0.5); run(saved, 0.5);
|
||||
assert.deepEqual(JSON.parse(JSON.stringify(c)), saved, 'ripresa identica dopo il passaggio JSON');
|
||||
}
|
||||
});
|
||||
function doubleLockAfterReturn(c) {
|
||||
for (const b of m.activeBalls(c)) Object.assign(b, { x: 320, y: 348, vx: 0, vy: 300, ramp: null, releaseAt: 0 });
|
||||
m.update(c, m.STEP); doubleLock(c); rampShot(c, 'super');
|
||||
}
|
||||
|
||||
test('il ball save nel futuro salva una sola volta, conserva campo e sigilli, poi torna al classico', () => {
|
||||
const c = inFuture(); c.extraBalls = []; c.future.hits.ponte = 1;
|
||||
c.saveUntil = c.sim + 2; const lives = c.balls;
|
||||
put(c, { x: 320, y: 348, vx: 0, vy: 300, ramp: null }); m.update(c, m.STEP);
|
||||
assert.equal(c.table, 'future'); assert.equal(c.balls, lives); assert.ok(m.waiting(c)); assert.equal(c.future.hits.ponte, 1);
|
||||
m.launch(c, 0.7); assert.equal(m.saveLeft(c), 0);
|
||||
put(c, { x: 320, y: 348, vx: 0, vy: 300, ramp: null }); m.update(c, m.STEP);
|
||||
assert.equal(c.table, 'classic'); assert.equal(c.balls, lives - 1);
|
||||
});
|
||||
|
||||
test('i tre percorsi del futuro sono raggiungibili con il colpo fisico della paletta', () => {
|
||||
for (const [id, x, y] of [['orbita', 288, 300], ['ponte', 290, 306], ['spira', 308, 320]]) {
|
||||
const c = m.newFlipper(9);
|
||||
Object.assign(c, m.futureLayout(), { table: 'future', future: { hits: { orbita: 0, ponte: 0, spira: 0 } } });
|
||||
m.launch(c, 0.8); c.saveUntil = 0;
|
||||
put(c, { x, y, vx: 0, vy: 180, inPlay: true }); m.hold(c, 'left', 'test', true);
|
||||
let entered = null;
|
||||
for (let i = 0; i < 160; i++) { m.update(c, m.STEP); if (c.ball?.ramp) { entered = c.ball.ramp.id; break; } }
|
||||
assert.equal(entered, id, 'il tiro parte dalla paletta, non dall’imbocco');
|
||||
}
|
||||
});
|
||||
|
||||
test('salvare l’avventura durante il futuro conserva il checkpoint, come negli altri minigiochi', () => {
|
||||
const g = new Game(story); g.go('via_provinciale'); settle(g);
|
||||
g.act('use', 'river'); settle(g); g.choose(g.options().findIndex(o => o.text.startsWith('Una partita alla bona'))); settle(g);
|
||||
const checkpoint = structuredClone(g.checkpoint);
|
||||
Object.assign(g.closeup, inFuture('bona'));
|
||||
const before = structuredClone(g.closeup), save = JSON.parse(JSON.stringify(g.savable()));
|
||||
assert.deepEqual(save, checkpoint); assert.deepEqual(g.closeup, before, 'salvare non consuma sigilli o palline');
|
||||
const loaded = new Game(story, { state: save });
|
||||
assert.equal(loaded.state.scene, 'via_provinciale'); assert.equal(loaded.closeup, null);
|
||||
g.closeupCancel(); assert.equal(g.closeup.table, 'future');
|
||||
g.closeupClose(); settle(g); assert.ok(g.state.flags.flipper_vinto, 'il traguardo resta acquisito uscendo dal futuro');
|
||||
});
|
||||
|
||||
@@ -24,10 +24,13 @@ test('il brano giusto per titolo, fine e scena', () => {
|
||||
assert.equal(trackFor({ ...s, scenes: { ...s.scenes, i: { indoor: true } } }, { screen: 'game', scene: 'i', phase: sera }), 'g.mp3');
|
||||
});
|
||||
|
||||
test('il flipper del River ha la sua musica, e il file c\'è', () => {
|
||||
test('il flipper del River ha la sua musica, e un\'altra la tavola del futuro', () => {
|
||||
const track = trackFor(story, { screen: 'game', scene: 'river', closeup: 'flipper' });
|
||||
assert.equal(track, 'assets/audio/flipper.mp3');
|
||||
assert.ok(existsSync(track));
|
||||
assert.equal(trackFor(story, { screen: 'game', scene: 'river', closeup: 'flipper', table: 'classic' }), track);
|
||||
const future = trackFor(story, { screen: 'game', scene: 'river', closeup: 'flipper', table: 'future' });
|
||||
assert.equal(future, 'assets/audio/flipper-futuro.mp3', 'il Regolo del futuro ha la sua musica');
|
||||
for (const f of [track, future]) assert.ok(existsSync(f), f);
|
||||
});
|
||||
|
||||
test('volume e muto', () => {
|
||||
|
||||
Reference in New Issue
Block a user