diff --git a/achievements.json b/achievements.json new file mode 100644 index 0000000..a5eba7b --- /dev/null +++ b/achievements.json @@ -0,0 +1,59 @@ +[ + { + "id": "first_steps", + "name": "First Steps", + "description": "Complete your first level", + "icon": "🏆", + "unlocked": false + }, + { + "id": "speed_demon", + "name": "Speed Demon", + "description": "Complete a level in under 60 seconds", + "icon": "⚡", + "unlocked": false + }, + { + "id": "no_hints", + "name": "Solo Hacker", + "description": "Complete a level without using hints", + "icon": "🧠", + "unlocked": false + }, + { + "id": "command_master", + "name": "Command Master", + "description": "Use at least 10 different commands in one level", + "icon": "💻", + "unlocked": false + }, + { + "id": "persistence", + "name": "Persistence", + "description": "Try at least 20 commands in a single level", + "icon": "🔨", + "unlocked": false + }, + { + "id": "explorer", + "name": "Explorer", + "description": "Visit all directories in a file system level", + "icon": "🧭", + "unlocked": false + }, + { + "id": "easter_egg_hunter", + "name": "Easter Egg Hunter", + "description": "Find a hidden secret", + "icon": "🥚", + "secret": true, + "unlocked": false + }, + { + "id": "master_hacker", + "name": "Master Hacker", + "description": "Complete the game", + "icon": "👑", + "unlocked": false + } +] \ No newline at end of file diff --git a/package.json b/package.json index 6c13b92..d0d5b4e 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "bun-types": "latest" }, "dependencies": { + "beep": "^0.0.0", "figlet": "^1.6.0", "figlet-cli": "^0.2.0", "gradient-string": "^2.0.2", diff --git a/src/core/achievements.ts b/src/core/achievements.ts new file mode 100644 index 0000000..e15fca9 --- /dev/null +++ b/src/core/achievements.ts @@ -0,0 +1,366 @@ +import fs from 'fs/promises'; +import path from 'path'; +import { getCurrentGameState } from './gameState'; +import { getTheme, successAnimation } from '../ui/visualEffects'; +import { playSound } from '../ui/soundEffects'; + +// Define achievement types +export interface Achievement { + id: string; + name: string; + description: string; + icon: string; + secret?: boolean; + unlocked: boolean; + unlockedAt?: number; +} + +// Define all achievements +export const achievements: Achievement[] = [ + { + id: 'first_steps', + name: 'First Steps', + description: 'Complete your first level', + icon: '🏆', + unlocked: false + }, + { + id: 'speed_demon', + name: 'Speed Demon', + description: 'Complete a level in under 60 seconds', + icon: '⚡', + unlocked: false + }, + { + id: 'no_hints', + name: 'Solo Hacker', + description: 'Complete a level without using hints', + icon: '🧠', + unlocked: false + }, + { + id: 'command_master', + name: 'Command Master', + description: 'Use at least 10 different commands in one level', + icon: '💻', + unlocked: false + }, + { + id: 'persistence', + name: 'Persistence', + description: 'Try at least 20 commands in a single level', + icon: '🔨', + unlocked: false + }, + { + id: 'explorer', + name: 'Explorer', + description: 'Visit all directories in a file system level', + icon: '🧭', + unlocked: false + }, + { + id: 'easter_egg_hunter', + name: 'Easter Egg Hunter', + description: 'Find a hidden secret', + icon: '🥚', + secret: true, + unlocked: false + }, + { + id: 'master_hacker', + name: 'Master Hacker', + description: 'Complete the game', + icon: '👑', + unlocked: false + } +]; + +// Path to achievements file +const achievementsPath = path.join(process.cwd(), 'achievements.json'); + +// Load achievements from file +export async function loadAchievements(): Promise { + try { + const data = await fs.readFile(achievementsPath, 'utf-8'); + return JSON.parse(data); + } catch (error) { + // If file doesn't exist, create it with default achievements + await saveAchievements(achievements); + return achievements; + } +} + +// Save achievements to file +export async function saveAchievements(achievements: Achievement[]): Promise { + try { + await fs.writeFile(achievementsPath, JSON.stringify(achievements, null, 2)); + } catch (error) { + console.error('Error saving achievements:', error); + } +} + +// Get player achievements +export async function getPlayerAchievements(playerName: string): Promise { + const allAchievements = await loadAchievements(); + + // Filter achievements for this player (in a real game, you'd store player-specific achievements) + return allAchievements; +} + +// Unlock an achievement +export async function unlockAchievement(achievementId: string): Promise { + const gameState = getCurrentGameState(); + if (!gameState) return false; + + // Load achievements + const allAchievements = await loadAchievements(); + const achievement = allAchievements.find(a => a.id === achievementId); + + if (!achievement || achievement.unlocked) { + return false; // Achievement doesn't exist or is already unlocked + } + + // Unlock the achievement + achievement.unlocked = true; + achievement.unlockedAt = Date.now(); + + // Save achievements + await saveAchievements(allAchievements); + + // Show achievement notification + const theme = getTheme(); + console.log('\n'); + console.log('╔' + '═'.repeat(50) + '╗'); + console.log('║ ' + theme.accent('Achievement Unlocked!').padEnd(48) + ' ║'); + console.log('║ ' + `${achievement.icon} ${achievement.name}`.padEnd(48) + ' ║'); + console.log('║ ' + achievement.description.padEnd(48) + ' ║'); + console.log('╚' + '═'.repeat(50) + '╝'); + + // Play sound + playSound('success'); + + return true; +} + +// Check and potentially unlock achievements based on game events +export async function checkAchievements(event: string, data?: any): Promise { + const gameState = getCurrentGameState(); + if (!gameState) return; + + switch (event) { + case 'level_completed': + // First Steps achievement + await unlockAchievement('first_steps'); + + // Speed Demon achievement + const levelState = gameState.levelStates[gameState.currentLevel]; + if (levelState && levelState.startTime) { + const completionTime = Date.now() - levelState.startTime; + if (completionTime < 60000) { // Less than 60 seconds + await unlockAchievement('speed_demon'); + } + } + + // No Hints achievement + if (levelState && !levelState.usedHint) { + await unlockAchievement('no_hints'); + } + + // Command Master achievement + if (levelState && levelState.uniqueCommands && levelState.uniqueCommands.size >= 10) { + await unlockAchievement('command_master'); + } + + // Persistence achievement + if (levelState && levelState.commandCount && levelState.commandCount >= 20) { + await unlockAchievement('persistence'); + } + + // Master Hacker achievement (game completed) + const allLevels = data?.allLevels || []; + if (gameState.currentLevel >= allLevels.length) { + await unlockAchievement('master_hacker'); + } + break; + + case 'hint_used': + // Mark that hints were used for this level + const currentLevel = gameState.currentLevel; + if (!gameState.levelStates[currentLevel]) { + gameState.levelStates[currentLevel] = {}; + } + gameState.levelStates[currentLevel].usedHint = true; + break; + + case 'command_used': + // Track unique commands used + const level = gameState.currentLevel; + if (!gameState.levelStates[level]) { + gameState.levelStates[level] = {}; + } + + if (!gameState.levelStates[level].uniqueCommands) { + gameState.levelStates[level].uniqueCommands = new Set(); + } + + if (!gameState.levelStates[level].commandCount) { + gameState.levelStates[level].commandCount = 0; + } + + gameState.levelStates[level].uniqueCommands.add(data.command); + gameState.levelStates[level].commandCount++; + break; + + case 'easter_egg_found': + await unlockAchievement('easter_egg_hunter'); + break; + + case 'all_directories_visited': + await unlockAchievement('explorer'); + break; + } +} + +// Display achievements screen +export async function showAchievements(): Promise { + const gameState = getCurrentGameState(); + if (!gameState) return; + + const theme = getTheme(); + const allAchievements = await loadAchievements(); + + console.clear(); + console.log(theme.accent('=== Achievements ===')); + console.log(''); + + // Group achievements by unlocked status + const unlockedAchievements = allAchievements.filter(a => a.unlocked); + const lockedAchievements = allAchievements.filter(a => !a.unlocked && !a.secret); + const secretAchievements = allAchievements.filter(a => !a.unlocked && a.secret); + + // Display unlocked achievements + console.log(theme.success('Unlocked Achievements:')); + if (unlockedAchievements.length === 0) { + console.log(' None yet. Keep playing!'); + } else { + unlockedAchievements.forEach(a => { + console.log(` ${a.icon} ${theme.accent(a.name)} - ${a.description}`); + }); + } + + console.log(''); + + // Display locked achievements + console.log(theme.secondary('Locked Achievements:')); + if (lockedAchievements.length === 0) { + console.log(' You\'ve unlocked all regular achievements!'); + } else { + lockedAchievements.forEach(a => { + console.log(` ${a.icon} ${theme.accent(a.name)} - ${a.description}`); + }); + } + + console.log(''); + + // Display secret achievements (just show that they exist) + console.log(theme.warning('Secret Achievements:')); + secretAchievements.forEach(a => { + console.log(` ${a.icon} ${theme.accent('???')} - Find this secret achievement!`); + }); + + console.log(''); + console.log(`Total Progress: ${unlockedAchievements.length}/${allAchievements.length} achievements unlocked`); +} + +// Add this function to the achievements.ts file +export async function triggerAchievement( + eventType: 'level_completed' | 'hint_used' | 'command_used' | 'easter_egg_found' | 'all_directories_visited', + data: any = {} +): Promise { + const gameState = getCurrentGameState(); + if (!gameState) return; + + // Process the event and check for achievements + switch (eventType) { + case 'level_completed': + // First level completion + if (data.levelId === 1) { + await unlockAchievement('first_steps'); + } + + // Complete a level quickly + if (data.timeSpent && data.timeSpent < 60) { + await unlockAchievement('speed_demon'); + } + + // Complete a level without hints + if (!data.usedHint) { + await unlockAchievement('no_hints'); + } + + // Complete all levels + if (data.levelId === data.allLevels) { + await unlockAchievement('master_hacker'); + } + break; + + case 'hint_used': + // Track hint usage + const currentLevel = gameState.currentLevel; + if (!gameState.levelStates[currentLevel]) { + gameState.levelStates[currentLevel] = {}; + } + gameState.levelStates[currentLevel].usedHint = true; + break; + + case 'command_used': + // Track unique commands used + const level = gameState.currentLevel; + if (!gameState.levelStates[level]) { + gameState.levelStates[level] = {}; + } + + if (!gameState.levelStates[level].uniqueCommands) { + gameState.levelStates[level].uniqueCommands = new Set(); + } + + if (!gameState.levelStates[level].commandCount) { + gameState.levelStates[level].commandCount = 0; + } + + gameState.levelStates[level].uniqueCommands.add(data.command); + gameState.levelStates[level].commandCount++; + + // Check for command master achievement + if (gameState.levelStates[level].uniqueCommands.size >= 10) { + await unlockAchievement('command_master'); + } + + // Check for persistence achievement + if (gameState.levelStates[level].commandCount >= 20) { + await unlockAchievement('persistence'); + } + break; + + case 'easter_egg_found': + await unlockAchievement('easter_egg_hunter'); + break; + + case 'all_directories_visited': + await unlockAchievement('explorer'); + break; + } +} + +// Add this function to initialize achievements +export async function initializeAchievements(): Promise { + try { + // Check if achievements file exists, if not create it + if (!fs.existsSync(achievementsPath)) { + await fs.writeFile(achievementsPath, JSON.stringify(achievements, null, 2)); + } + } catch (error) { + console.error('Error initializing achievements:', error); + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 5e6887b..823af16 100755 --- a/src/index.ts +++ b/src/index.ts @@ -3,15 +3,19 @@ import { renderMainMenu } from './ui/mainMenu'; import { initializeGame } from './core/gameInit'; import { registerAllLevels } from './levels'; +import { initializeAchievements } from './core/achievements'; async function main() { // Initialize game systems await initializeGame(); + // Initialize achievements + await initializeAchievements(); + // Register all game levels registerAllLevels(); - // Render the main menu to start (which now includes the boot sequence) + // Render the main menu to start await renderMainMenu(); } diff --git a/src/ui/commandHistory.ts b/src/ui/commandHistory.ts new file mode 100644 index 0000000..5074d7f --- /dev/null +++ b/src/ui/commandHistory.ts @@ -0,0 +1,87 @@ +// Maximum number of commands to store in history +const MAX_HISTORY_SIZE = 50; + +// Command history for each player +const commandHistories: Record = {}; +let currentHistoryIndex = -1; +let currentInput = ''; + +// Initialize command history for a player +export function initCommandHistory(playerName: string): void { + if (!commandHistories[playerName]) { + commandHistories[playerName] = []; + } + currentHistoryIndex = -1; + currentInput = ''; +} + +// Add a command to history +export function addToHistory(playerName: string, command: string): void { + if (!commandHistories[playerName]) { + initCommandHistory(playerName); + } + + // Don't add empty commands or duplicates of the last command + if (command.trim() === '' || + (commandHistories[playerName].length > 0 && + commandHistories[playerName][0] === command)) { + return; + } + + // Add to the beginning of the array + commandHistories[playerName].unshift(command); + + // Trim history if it gets too long + if (commandHistories[playerName].length > MAX_HISTORY_SIZE) { + commandHistories[playerName].pop(); + } + + // Reset index + currentHistoryIndex = -1; + currentInput = ''; +} + +// Get previous command from history +export function getPreviousCommand(playerName: string, currentCommand: string): string { + if (!commandHistories[playerName] || commandHistories[playerName].length === 0) { + return currentCommand; + } + + // Save current input if we're just starting to navigate history + if (currentHistoryIndex === -1) { + currentInput = currentCommand; + } + + // Move back in history + currentHistoryIndex = Math.min(currentHistoryIndex + 1, commandHistories[playerName].length - 1); + return commandHistories[playerName][currentHistoryIndex]; +} + +// Get next command from history +export function getNextCommand(playerName: string): string { + if (!commandHistories[playerName] || currentHistoryIndex === -1) { + return currentInput; + } + + // Move forward in history + currentHistoryIndex = Math.max(currentHistoryIndex - 1, -1); + + // Return original input if we've reached the end of history + if (currentHistoryIndex === -1) { + return currentInput; + } + + return commandHistories[playerName][currentHistoryIndex]; +} + +// Get all commands in history +export function getCommandHistory(playerName: string): string[] { + return commandHistories[playerName] || []; +} + +// Clear command history +export function clearCommandHistory(playerName: string): void { + commandHistories[playerName] = []; + currentHistoryIndex = -1; + currentInput = ''; +} \ No newline at end of file diff --git a/src/ui/gameUI.ts b/src/ui/gameUI.ts index f5800a3..089ac38 100644 --- a/src/ui/gameUI.ts +++ b/src/ui/gameUI.ts @@ -10,6 +10,8 @@ import { } from './visualEffects'; import { playSound } from './soundEffects'; import { levelUI } from './levelRenderer'; +import { addToHistory } from './commandHistory'; +import { triggerAchievement } from '../core/achievements'; export async function renderGameUI(): Promise { const gameState = getCurrentGameState(); @@ -54,6 +56,10 @@ export async function renderGameUI(): Promise { levelUI.inputBox(); const input = await promptInput(''); + if (input.trim()) { + addToHistory(gameState.playerName, input); + } + // Handle special commands if (input.startsWith('/')) { const command = input.slice(1).toLowerCase(); @@ -76,6 +82,7 @@ export async function renderGameUI(): Promise { } if (command === 'hint') { + await triggerAchievement('hint_used'); await showHint(currentLevel.hints); continue; } @@ -93,6 +100,15 @@ export async function renderGameUI(): Promise { if (result.completed) { playSound('levelComplete'); await completeCurrentLevel(); + + // Trigger level completion achievement + await triggerAchievement('level_completed', { + levelId: gameState.currentLevel, + usedHint: gameState.levelStates[gameState.currentLevel]?.usedHint || false, + timeSpent: gameState.levelStates[gameState.currentLevel]?.timeSpent || 0, + allLevels: getAllLevels().length + }); + await successAnimation('Level completed!'); if (result.nextAction === 'main_menu') { @@ -116,6 +132,9 @@ export async function renderGameUI(): Promise { } } } + + // When using a command, track it for achievements + await triggerAchievement('command_used', { command: input }); } } diff --git a/src/ui/mainMenu.ts b/src/ui/mainMenu.ts index 32a06b6..b262dd3 100644 --- a/src/ui/mainMenu.ts +++ b/src/ui/mainMenu.ts @@ -14,10 +14,24 @@ import { successAnimation, loadingAnimation } from './visualEffects'; +import { showAchievements } from '../core/achievements'; +import { renderProgressMap } from './progressMap'; +import { + toggleSound, + toggleAmbientSound, + toggleSoundEffects, + setSoundVolume, + soundConfig, + initSoundSystem +} from './soundEffects'; +import { addToHistory } from './commandHistory'; // Track if we've shown the boot sequence let bootSequenceShown = false; +// Initialize sound system in the main menu +initSoundSystem(); + export async function renderMainMenu(): Promise { // Show boot sequence only once if (!bootSequenceShown) { @@ -41,8 +55,10 @@ export async function renderMainMenu(): Promise { '1. ' + theme.accent('New Game'), '2. ' + theme.accent('Load Game'), '3. ' + theme.accent('Leaderboard'), - '4. ' + theme.accent('Settings'), - '5. ' + theme.accent('Exit') + '4. ' + theme.accent('Achievements'), + '5. ' + theme.accent('Progress Map'), + '6. ' + theme.accent('Settings'), + '7. ' + theme.accent('Exit') ]; console.log(drawBox('MAIN MENU', menuOptions.join('\n'))); @@ -61,9 +77,18 @@ export async function renderMainMenu(): Promise { await showLeaderboard(); break; case '4': - await showSettings(); + await showAchievements(); + await promptInput('Press Enter to continue...'); break; case '5': + clearScreen(); + renderProgressMap(); + await promptInput('Press Enter to continue...'); + break; + case '6': + await showSettings(); + break; + case '7': await animateText('Thanks for playing Terminal Escape!', 30); process.exit(0); default: @@ -204,4 +229,69 @@ async function showLeaderboard(): Promise { console.log(''); await promptInput('Press Enter to return to main menu...'); +} + +// Add this function to the mainMenu.ts file +async function soundSettings(): Promise { + const theme = getTheme(); + + while (true) { + clearScreen(); + console.log(theme.accent('=== SOUND SETTINGS ===')); + console.log(''); + + console.log(`1. Sound: ${soundConfig.enabled ? theme.success('ON') : theme.error('OFF')}`); + console.log(`2. Ambient Sound: ${soundConfig.ambientEnabled ? theme.success('ON') : theme.error('OFF')}`); + console.log(`3. Sound Effects: ${soundConfig.effectsEnabled ? theme.success('ON') : theme.error('OFF')}`); + console.log(`4. Volume: ${Math.round(soundConfig.volume * 100)}%`); + console.log('5. Back to Settings'); + console.log(''); + + const choice = await promptInput('Select an option: '); + + switch (choice) { + case '1': + toggleSound(); + break; + case '2': + toggleAmbientSound(); + break; + case '3': + toggleSoundEffects(); + break; + case '4': + await changeVolume(); + break; + case '5': + return; + default: + console.log(theme.error('Invalid option. Press Enter to continue...')); + await promptInput(''); + } + } +} + +// Add this function to change volume +async function changeVolume(): Promise { + const theme = getTheme(); + + clearScreen(); + console.log(theme.accent('=== VOLUME SETTINGS ===')); + console.log(''); + + console.log('Current volume: ' + Math.round(soundConfig.volume * 100) + '%'); + console.log('Enter a value between 0 and 100:'); + + const input = await promptInput(''); + const volume = parseInt(input); + + if (isNaN(volume) || volume < 0 || volume > 100) { + console.log(theme.error('Invalid volume. Please enter a number between 0 and 100.')); + await promptInput('Press Enter to continue...'); + return; + } + + setSoundVolume(volume / 100); + console.log(theme.success(`Volume set to ${volume}%`)); + await promptInput('Press Enter to continue...'); } \ No newline at end of file diff --git a/src/ui/progressMap.ts b/src/ui/progressMap.ts new file mode 100644 index 0000000..065fab1 --- /dev/null +++ b/src/ui/progressMap.ts @@ -0,0 +1,73 @@ +import { getAllLevels, getLevelById } from '../core/levelSystem'; +import { getCurrentGameState } from '../core/gameState'; +import { getTheme } from './visualEffects'; + +export function renderProgressMap(): void { + const gameState = getCurrentGameState(); + if (!gameState) return; + + const theme = getTheme(); + const allLevels = getAllLevels(); + const currentLevelId = gameState.currentLevel; + + console.log(theme.accent('=== Progress Map ===')); + console.log(''); + + // Calculate the maximum level name length for formatting + const maxNameLength = Math.max(...allLevels.map(level => level.name.length)); + + // Create a visual map of levels + console.log('┌' + '─'.repeat(maxNameLength + 22) + '┐'); + + allLevels.forEach((level, index) => { + const levelNumber = level.id; + const isCurrentLevel = levelNumber === currentLevelId; + const isCompleted = levelNumber < currentLevelId; + const isLocked = levelNumber > currentLevelId; + + let statusIcon; + let levelName; + + if (isCompleted) { + statusIcon = theme.success('✓'); + levelName = theme.success(level.name.padEnd(maxNameLength)); + } else if (isCurrentLevel) { + statusIcon = theme.accent('▶'); + levelName = theme.accent(level.name.padEnd(maxNameLength)); + } else if (isLocked) { + statusIcon = theme.secondary('🔒'); + levelName = theme.secondary(level.name.padEnd(maxNameLength)); + } + + console.log(`│ ${statusIcon} Level ${levelNumber.toString().padStart(2)} │ ${levelName} │`); + + // Add connector line between levels + if (index < allLevels.length - 1) { + console.log('│ ' + ' '.repeat(maxNameLength + 20) + '│'); + console.log('│ ' + theme.secondary('│').padStart(7) + ' '.repeat(maxNameLength + 14) + '│'); + console.log('│ ' + theme.secondary('▼').padStart(7) + ' '.repeat(maxNameLength + 14) + '│'); + console.log('│ ' + ' '.repeat(maxNameLength + 20) + '│'); + } + }); + + console.log('└' + '─'.repeat(maxNameLength + 22) + '┘'); + + // Show completion percentage + const completedLevels = Math.max(0, currentLevelId - 1); + const completionPercentage = Math.round((completedLevels / allLevels.length) * 100); + + console.log(''); + console.log(`Overall Progress: ${completedLevels}/${allLevels.length} levels completed (${completionPercentage}%)`); + + // Visual progress bar + const progressBarWidth = 40; + const filledWidth = Math.round((completionPercentage / 100) * progressBarWidth); + const emptyWidth = progressBarWidth - filledWidth; + + const progressBar = '[' + + theme.success('='.repeat(filledWidth)) + + theme.secondary('-'.repeat(emptyWidth)) + + '] ' + completionPercentage + '%'; + + console.log(progressBar); +} \ No newline at end of file diff --git a/src/ui/soundEffects.ts b/src/ui/soundEffects.ts index fceea91..cc0756e 100644 --- a/src/ui/soundEffects.ts +++ b/src/ui/soundEffects.ts @@ -1,20 +1,49 @@ -import player from 'play-sound'; +// Simplified sound effects module that doesn't actually play sounds +// but maintains the interface for the rest of the application -const audioPlayer = player({}); +export const soundConfig = { + enabled: false, + volume: 0.5, + ambientEnabled: false, + effectsEnabled: false +}; +// Play a sound effect (does nothing) export function playSound(sound: 'success' | 'error' | 'typing' | 'levelComplete'): void { - try { - const soundMap = { - success: 'sounds/success.wav', - error: 'sounds/error.wav', - typing: 'sounds/typing.wav', - levelComplete: 'sounds/level-complete.wav' - }; - - audioPlayer.play(soundMap[sound], (err) => { - if (err) console.error('Error playing sound:', err); - }); - } catch (error) { - // Silently fail if sound can't be played - } + // No-op function to maintain API compatibility +} + +// Play ambient sound (does nothing) +export function playAmbientSound(): void { + // No-op function to maintain API compatibility +} + +// Stop the ambient sound (does nothing) +export function stopAmbientSound(): void { + // No-op function to maintain API compatibility +} + +// Toggle sound on/off +export function toggleSound(): boolean { + return soundConfig.enabled; +} + +// Toggle ambient sound on/off +export function toggleAmbientSound(): boolean { + return soundConfig.ambientEnabled; +} + +// Toggle sound effects on/off +export function toggleSoundEffects(): boolean { + return soundConfig.effectsEnabled; +} + +// Set sound volume +export function setSoundVolume(volume: number): void { + soundConfig.volume = Math.max(0, Math.min(1, volume)); +} + +// Initialize sound system (does nothing) +export function initSoundSystem(): void { + // No-op function to maintain API compatibility } \ No newline at end of file diff --git a/src/ui/uiHelpers.ts b/src/ui/uiHelpers.ts index 9209a21..f41e7b2 100644 --- a/src/ui/uiHelpers.ts +++ b/src/ui/uiHelpers.ts @@ -1,5 +1,7 @@ import readline from 'readline'; import kleur from 'kleur'; +import { getCurrentGameState } from '../core/gameState'; +import { getPreviousCommand, getNextCommand } from './commandHistory'; // Enable colors kleur.enabled = true;