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-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/sam-js@0.3.1/dist/samjs.min.js"></script>
|
||||
<script src="boot.js"></script>
|
||||
<script type="module" src="terminal.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -3,6 +3,42 @@
|
||||
* 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
|
||||
* @returns {void}
|
||||
@@ -746,8 +782,10 @@ export function processGameInput(term, input) {
|
||||
term.writeln('');
|
||||
if (matched) {
|
||||
gameMode.matchesCleared++;
|
||||
samSpeak('match');
|
||||
|
||||
if (game.isComplete()) {
|
||||
samSpeak('you win');
|
||||
term.writeln(' \x1b[32m╔════════════════════════════════════╗\x1b[0m');
|
||||
term.writeln(' \x1b[32m║ CONGRATULATIONS! YOU WON! ║\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`);
|
||||
} else {
|
||||
samSpeak('no');
|
||||
if (v1 !== v2 && v1 + v2 !== 10) {
|
||||
renderBoard(term, game, null, `\x1b[31m${v1} and ${v2} don't match\x1b[0m`);
|
||||
} else {
|
||||
@@ -809,6 +848,7 @@ export function startGame(term) {
|
||||
term.writeln(' \x1b[90mCommands: add, hint, new, quit\x1b[0m');
|
||||
term.writeln('');
|
||||
|
||||
samSpeak('Number Match');
|
||||
renderBoard(term, gameMode.game, null, '');
|
||||
}
|
||||
|
||||
@@ -839,6 +879,7 @@ export function handleTileClick(coord) {
|
||||
if (gameMode.selectedIndex === null) {
|
||||
gameMode.selectedIndex = clickedIndex;
|
||||
playClickSound();
|
||||
samSpeak(String(tiles[clickedIndex]));
|
||||
updateBoardInPlace(term, game, clickedIndex, `\x1b[33mSelected ${coord}. Click another to match.\x1b[0m`);
|
||||
return;
|
||||
}
|
||||
@@ -863,10 +904,12 @@ export function handleTileClick(coord) {
|
||||
if (matched) {
|
||||
gameMode.matchesCleared++;
|
||||
playMatchSound();
|
||||
samSpeak('match');
|
||||
|
||||
if (game.isComplete()) {
|
||||
// Game won - need to redraw fresh for victory screen
|
||||
playMatchSound();
|
||||
samSpeak('you win');
|
||||
term.write(`\x1b[${gameMode.boardLines}A`);
|
||||
for (let i = 0; i < gameMode.boardLines; i++) {
|
||||
term.writeln('\x1b[2K');
|
||||
@@ -885,6 +928,7 @@ export function handleTileClick(coord) {
|
||||
updateBoardInPlace(term, game, null, `\x1b[32mMatch! ${v1}+${v2}\x1b[0m`);
|
||||
} else {
|
||||
playErrorSound();
|
||||
samSpeak('no');
|
||||
if (v1 !== v2 && v1 + v2 !== 10) {
|
||||
updateBoardInPlace(term, game, null, `\x1b[31m${v1} and ${v2} don't match or sum to 10\x1b[0m`);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user