orbit/dev/comms-types.mjs

123 lines
5.5 KiB
JavaScript

/**
* Dev-only: exercise the VISUAL + AUDIO comm paths in the live GameScene.
* The shipped roster is ack-only (the newGame welcome); this page overrides
* the comms config at boot so 'newGame' arms, in FIFO order:
*
* t-visual → box opens, clip plays ONCE (loop=false), NO ACK button,
* NO pause (comms.paused stays false, the ship is not parked),
* and the comm FINISHES ON ITS OWN when the voice ends
* (the isPlaying poll → visualHoldMs → fade).
* t-audio → NO box at all (comms.box is null), voice plays, no pause,
* finishes on its own.
*
* Headless note: the AudioContext is suspended, so the 12.6 s clip never
* reaches its natural end — the test stops the voice with stopByKey(),
* which is exactly what the hub's isPlaying poll sees as "the voice ended"
* (isPlaying → false). The hub must then finish the comm by itself.
*
* node dev/cdp-shot.mjs "http://127.0.0.1:8090/dev/comms-types.html" o.png \
* "window.__commsTypes" 90000
*/
import Phaser from '../js/vendor/phaser.js';
import { config } from '../js/config/Config.js';
import { ConfigLoader } from '../js/config/ConfigLoader.js';
import { createGameConfig } from '../js/config/GameConfig.js';
import { GameScene } from '../js/scenes/GameScene.js';
const data = await ConfigLoader.load();
// --- override: two newGame comms — one visual, one audio ---------------
const voice = 'assets/speech/oac/intro-01.mp3';
data.comms = {
...(data.comms ?? {}),
comms: [
{
id: 't-visual', from: 'oac', type: 'visual', trigger: 'newGame',
text: 'A short visual comm — the action should keep running.',
audio: voice, once: true,
},
{
id: 't-audio', from: 'oac', type: 'audio', trigger: 'newGame',
audio: voice, once: true, // second in the queue (FIFO behind the visual)
},
],
};
config.init(data);
const gameConfig = createGameConfig();
gameConfig.scene = [GameScene];
const game = new Phaser.Game(gameConfig);
window.game = game;
const report = document.createElement('pre');
report.id = 'report';
report.style.cssText = 'position:fixed;left:10px;top:10px;z-index:9999;margin:0;padding:8px 12px;font:12px/1.5 monospace;color:#eaf6ff;background:rgba(6,20,16,0.9);border:1px solid #1b3a5a;max-width:55%;white-space:pre-wrap;';
document.body.appendChild(report);
const log = (m) => { report.textContent += m + '\n'; };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const until = async (fn, ms = 20000) => {
const t0 = Date.now();
for (;;) {
const v = fn();
if (v) return v;
if (Date.now() - t0 > ms) return null;
await sleep(120);
}
};
setTimeout(async () => {
const d = [];
try {
const s = () => game.scene.getScene('GameScene');
const ready = await until(() => {
const sc = s();
return sc && sc.ship && sc.comms ? sc : null;
});
if (!ready) throw new Error('scene never ready');
// ---- VISUAL -------------------------------------------------------
const live = await until(() => (s().comms.active && s().comms.current?.id === 't-visual' ? s() : null));
if (!live) throw new Error('visual comm never came up');
const box = s().comms.box;
const el = box?.video?.video;
d.push(`visual: active=true paused=${s().comms.paused} (expect false) box=${!!box} button=${!!box?.button} (expect false)`);
d.push(`visual: clip loop=${el ? el.loop : '—'} (expect false) playing=${el ? el.paused === false : '—'}`);
d.push(`visual: shipFrozen=${s().ship.body.velocity.length() >= 1} (expect false — the comm did NOT park the ship)`);
const visualOk =
s().comms.paused === false && // NO pause (the visual contract)
!!box &&
box.button === null && // no ACKNOWLEDGE button
!!el &&
el.loop === false && // the clip plays ONCE
el.paused === false;
// The voice is talking; end it the way the hub's poll sees an end.
const vKey = await until(() => (s().comms._voiceKey && s().sound.isPlaying(s().comms._voiceKey) ? s().comms._voiceKey : null));
if (!vKey) throw new Error('visual voice never started');
s().sound.stopByKey(vKey);
const visualDone = await until(() => (s().comms.current?.id !== 't-visual' ? true : null), 5000);
d.push(`visual: voice=${vKey} auto-finished=${!!visualDone} boxGone=${s().comms.box === null} paused=${s().comms.paused}`);
const ok = visualOk && !!visualDone && s().comms.box === null;
// ---- AUDIO --------------------------------------------------------
const aLive = await until(() => (s().comms.active && s().comms.current?.id === 't-audio' ? s() : null), 10000);
if (!aLive) throw new Error('audio comm never came up');
const aNoBox = s().comms.box === null;
const aPaused = s().comms.paused;
const aVoiceKey = s().comms._voiceKey;
const aVoice = aVoiceKey ? s().sound.isPlaying(aVoiceKey) : null;
d.push(`audio: active=true box=${!!s().comms.box} (expect false) paused=${aPaused} (expect false) voice=${aVoice} (expect true)`);
if (aVoiceKey) s().sound.stopByKey(aVoiceKey);
const aDone = await until(() => (s().comms.current?.id !== 't-audio' ? true : null), 5000);
d.push(`audio: auto-finished=${!!aDone}`);
const okAll = ok && aNoBox && aPaused === false && aVoice === true && !!aDone;
window.__commsTypes = { ok: okAll, details: d };
console.log('[comms-types]', okAll ? 'PASS' : 'FAIL', d.join(' | '));
} catch (err) {
window.__commsTypes = { ok: false, details: [...d, 'EXCEPTION: ' + String(err?.stack ?? err)] };
console.error('[comms-types] FAIL', err);
}
}, 600);