mirror of
https://github.com/litruv/lit.ruv.wtf.git
synced 2026-07-24 02:36:02 +10:00
feat: integrate SAM speech synthesizer for Number Match game feedback
This commit is contained in:
@@ -69,6 +69,7 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-web-links@0.9.0/lib/xterm-addon-web-links.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-web-links@0.9.0/lib/xterm-addon-web-links.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/sam-js@0.3.1/dist/samjs.min.js"></script>
|
||||||
<script src="boot.js"></script>
|
<script src="boot.js"></script>
|
||||||
<script type="module" src="terminal.js"></script>
|
<script type="module" src="terminal.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -3,6 +3,42 @@
|
|||||||
* Match pairs of numbers that are equal or sum to 10
|
* Match pairs of numbers that are equal or sum to 10
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SAM (Software Automatic Mouth) speech synthesizer instance
|
||||||
|
* Uses "Little Robot" voice preset for retro game feel
|
||||||
|
* @type {object|null}
|
||||||
|
*/
|
||||||
|
let sam = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize SAM speech synthesizer with robot voice
|
||||||
|
* @returns {object|null} SAM instance or null if unavailable
|
||||||
|
*/
|
||||||
|
function initSam() {
|
||||||
|
if (sam) return sam;
|
||||||
|
if (typeof SamJs !== 'undefined') {
|
||||||
|
// Little Robot preset: speed=92, pitch=60, mouth=190, throat=190
|
||||||
|
sam = new SamJs({ speed: 92, pitch: 60, mouth: 190, throat: 190 });
|
||||||
|
}
|
||||||
|
return sam;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speak text using SAM
|
||||||
|
* @param {string} text - Text to speak
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function samSpeak(text) {
|
||||||
|
const samInstance = initSam();
|
||||||
|
if (samInstance) {
|
||||||
|
try {
|
||||||
|
samInstance.speak(text);
|
||||||
|
} catch (_err) {
|
||||||
|
// Silently fail if speech doesn't work
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Play a click/select sound
|
* Play a click/select sound
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
@@ -746,8 +782,10 @@ export function processGameInput(term, input) {
|
|||||||
term.writeln('');
|
term.writeln('');
|
||||||
if (matched) {
|
if (matched) {
|
||||||
gameMode.matchesCleared++;
|
gameMode.matchesCleared++;
|
||||||
|
samSpeak('match');
|
||||||
|
|
||||||
if (game.isComplete()) {
|
if (game.isComplete()) {
|
||||||
|
samSpeak('you win');
|
||||||
term.writeln(' \x1b[32m╔════════════════════════════════════╗\x1b[0m');
|
term.writeln(' \x1b[32m╔════════════════════════════════════╗\x1b[0m');
|
||||||
term.writeln(' \x1b[32m║ CONGRATULATIONS! YOU WON! ║\x1b[0m');
|
term.writeln(' \x1b[32m║ CONGRATULATIONS! YOU WON! ║\x1b[0m');
|
||||||
term.writeln(' \x1b[32m╚════════════════════════════════════╝\x1b[0m');
|
term.writeln(' \x1b[32m╚════════════════════════════════════╝\x1b[0m');
|
||||||
@@ -760,6 +798,7 @@ export function processGameInput(term, input) {
|
|||||||
}
|
}
|
||||||
renderBoard(term, game, null, `\x1b[32mMatch! ${v1}+${v2}\x1b[0m`);
|
renderBoard(term, game, null, `\x1b[32mMatch! ${v1}+${v2}\x1b[0m`);
|
||||||
} else {
|
} else {
|
||||||
|
samSpeak('no');
|
||||||
if (v1 !== v2 && v1 + v2 !== 10) {
|
if (v1 !== v2 && v1 + v2 !== 10) {
|
||||||
renderBoard(term, game, null, `\x1b[31m${v1} and ${v2} don't match\x1b[0m`);
|
renderBoard(term, game, null, `\x1b[31m${v1} and ${v2} don't match\x1b[0m`);
|
||||||
} else {
|
} else {
|
||||||
@@ -809,6 +848,7 @@ export function startGame(term) {
|
|||||||
term.writeln(' \x1b[90mCommands: add, hint, new, quit\x1b[0m');
|
term.writeln(' \x1b[90mCommands: add, hint, new, quit\x1b[0m');
|
||||||
term.writeln('');
|
term.writeln('');
|
||||||
|
|
||||||
|
samSpeak('Number Match');
|
||||||
renderBoard(term, gameMode.game, null, '');
|
renderBoard(term, gameMode.game, null, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,6 +879,7 @@ export function handleTileClick(coord) {
|
|||||||
if (gameMode.selectedIndex === null) {
|
if (gameMode.selectedIndex === null) {
|
||||||
gameMode.selectedIndex = clickedIndex;
|
gameMode.selectedIndex = clickedIndex;
|
||||||
playClickSound();
|
playClickSound();
|
||||||
|
samSpeak(String(tiles[clickedIndex]));
|
||||||
updateBoardInPlace(term, game, clickedIndex, `\x1b[33mSelected ${coord}. Click another to match.\x1b[0m`);
|
updateBoardInPlace(term, game, clickedIndex, `\x1b[33mSelected ${coord}. Click another to match.\x1b[0m`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -863,10 +904,12 @@ export function handleTileClick(coord) {
|
|||||||
if (matched) {
|
if (matched) {
|
||||||
gameMode.matchesCleared++;
|
gameMode.matchesCleared++;
|
||||||
playMatchSound();
|
playMatchSound();
|
||||||
|
samSpeak('match');
|
||||||
|
|
||||||
if (game.isComplete()) {
|
if (game.isComplete()) {
|
||||||
// Game won - need to redraw fresh for victory screen
|
// Game won - need to redraw fresh for victory screen
|
||||||
playMatchSound();
|
playMatchSound();
|
||||||
|
samSpeak('you win');
|
||||||
term.write(`\x1b[${gameMode.boardLines}A`);
|
term.write(`\x1b[${gameMode.boardLines}A`);
|
||||||
for (let i = 0; i < gameMode.boardLines; i++) {
|
for (let i = 0; i < gameMode.boardLines; i++) {
|
||||||
term.writeln('\x1b[2K');
|
term.writeln('\x1b[2K');
|
||||||
@@ -885,6 +928,7 @@ export function handleTileClick(coord) {
|
|||||||
updateBoardInPlace(term, game, null, `\x1b[32mMatch! ${v1}+${v2}\x1b[0m`);
|
updateBoardInPlace(term, game, null, `\x1b[32mMatch! ${v1}+${v2}\x1b[0m`);
|
||||||
} else {
|
} else {
|
||||||
playErrorSound();
|
playErrorSound();
|
||||||
|
samSpeak('no');
|
||||||
if (v1 !== v2 && v1 + v2 !== 10) {
|
if (v1 !== v2 && v1 + v2 !== 10) {
|
||||||
updateBoardInPlace(term, game, null, `\x1b[31m${v1} and ${v2} don't match or sum to 10\x1b[0m`);
|
updateBoardInPlace(term, game, null, `\x1b[31m${v1} and ${v2} don't match or sum to 10\x1b[0m`);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user