UNPKG

namastejs

Version:

A spiritual greeting from your JavaScript code. Because every function deserves a 'Namaste šŸ™'

150 lines (122 loc) • 3.68 kB
const readline = require("readline"); const { initScreen, updateHeader, renderBody, renderInput, parkCursor, BODY_HEIGHT, BODY_WIDTH, } = require("./renderer"); const { initInput } = require("./input"); const { WORDS, WORD_COLORS } = require("../data/words"); const { style } = require("../../../theme"); // šŸ”’ Hide cursor during game process.stdout.write("\x1B[?25l"); let words = []; let score = 0; let dropped = 0; let speed = 1000; // slow start let backspaceDisabled = false; let gameRunning = true; let isPaused = false; // ─── HELPERS ─────────────────────────────── function randomWord() { return WORDS[Math.floor(Math.random() * WORDS.length)]; } function randomColor() { return WORD_COLORS[Math.floor(Math.random() * WORD_COLORS.length)]; } function spawnWord() { const text = randomWord(); const x = Math.floor(Math.random() * (BODY_WIDTH - text.length - 2)); words.push({ text, x, y: 0, color: randomColor() }); } function restoreTerminal() { process.stdout.write("\x1B[?25h"); // show cursor back process.stdin.setRawMode(false); process.stdin.pause(); } function gameOver() { gameRunning = false; restoreTerminal(); process.stdout.write("\x1Bc"); console.log(style.red("\nšŸ’€ GAME OVER šŸ’€")); console.log(style.gold(`šŸ† Final Score: ${score}`)); console.log(style.cyan("šŸ™ Thanks for playing Typing Rain šŸ™\n")); process.exit(); } function togglePause() { isPaused = !isPaused; updateHeader({ score, lives: Math.max(0, 3 - dropped), dropped, isPaused }); } // ─── MAIN GAME LOOP (SMOOTH & DYNAMIC) ────── function gameLoop() { if (!gameRunning || isPaused) { setTimeout(gameLoop, speed); return; } // move words down words.forEach((w) => w.y++); // drop detection words = words.filter((w) => { if (w.y >= BODY_HEIGHT) { dropped++; if (dropped >= 3) gameOver(); return false; } return true; }); renderBody(words); updateHeader({ score, lives: Math.max(0, 3 - dropped), dropped, isPaused, }); parkCursor(); setTimeout(gameLoop, speed); } // ─── INPUT HANDLING (EXTERNALIZED) ────────── initInput({ onSubmit: (typedWord) => { const index = words.findIndex((w) => w.text === typedWord); if (index !== -1) { words.splice(index, 1); score++; // šŸ”„ Level-up rule if (score === 10 && !backspaceDisabled) { backspaceDisabled = true; speed -= 100; readline.cursorTo(process.stdout, 2, BODY_HEIGHT + 6); console.log("āš ļø Level Up! Backspace disabled"); parkCursor(); } // increase difficulty if (score === 15) speed -= 100; if (score === 25) speed -= 100; if (score === 30) speed -= 100; if (score === 35) speed -= 100; if (score === 40) speed -= 100; if (score === 45) speed -= 100; } }, onExit: gameOver, onInputChange: (input) => { renderInput(input); parkCursor(); }, onPauseToggle: togglePause, isBackspaceDisabled: () => backspaceDisabled, }); // ─── START GAME ───────────────────────────── initScreen(); updateHeader({ score, lives: 3, dropped, isPaused }); renderInput(""); parkCursor(); // spawn words independently spawnInterval = setInterval(() => { if (!isPaused) spawnWord(); }, 2000); // start falling loop gameLoop();