🎉 GOOD ANSWER ANIMATIONS: - Enhanced explosion with color transitions (blue→green→orange→red) - Screen shake effect for impact feedback - Floating points popup (+10, +12, etc.) with smooth animation - Gentle vibration pattern for positive reinforcement ❌ BAD ANSWER ANIMATIONS: - Red shake animation for all falling words - Answer panel flash with red glow effect - Full screen red overlay flash - Strong vibration pattern for negative feedback 🎨 TECHNICAL IMPROVEMENTS: - New CSS keyframes: explode, wrongShake, wrongFlash, screenShake, pointsFloat - Enhanced correctAnswer() method with screen shake and points popup - Enhanced wrongAnswer() method with multi-element animations - Vibration API integration for tactile feedback - Proper animation cleanup and timing 🎯 UX ENHANCEMENT: - Much more satisfying and engaging gameplay experience - Clear visual distinction between success and failure - Gamification elements that motivate continued play 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
781 lines
26 KiB
JavaScript
781 lines
26 KiB
JavaScript
// === LETTER DISCOVERY GAME ===
|
|
// Discover letters first, then explore words that start with each letter
|
|
|
|
class LetterDiscovery {
|
|
constructor({ container, content, onScoreUpdate, onGameEnd }) {
|
|
this.container = container;
|
|
this.content = content;
|
|
this.onScoreUpdate = onScoreUpdate;
|
|
this.onGameEnd = onGameEnd;
|
|
|
|
// Game state
|
|
this.currentPhase = 'letter-discovery'; // letter-discovery, word-exploration, practice
|
|
this.currentLetterIndex = 0;
|
|
this.discoveredLetters = [];
|
|
this.currentLetter = null;
|
|
this.currentWordIndex = 0;
|
|
this.discoveredWords = [];
|
|
this.score = 0;
|
|
this.lives = 3;
|
|
|
|
// Content processing
|
|
this.letters = [];
|
|
this.letterWords = {}; // Map letter -> words starting with that letter
|
|
|
|
// Practice system
|
|
this.practiceLevel = 1;
|
|
this.practiceRound = 0;
|
|
this.maxPracticeRounds = 8;
|
|
this.practiceCorrectAnswers = 0;
|
|
this.practiceErrors = 0;
|
|
this.currentPracticeItems = [];
|
|
|
|
this.injectCSS();
|
|
this.extractContent();
|
|
this.init();
|
|
}
|
|
|
|
injectCSS() {
|
|
if (document.getElementById('letter-discovery-styles')) return;
|
|
|
|
const styleSheet = document.createElement('style');
|
|
styleSheet.id = 'letter-discovery-styles';
|
|
styleSheet.textContent = `
|
|
.letter-discovery-wrapper {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
padding: 20px;
|
|
position: relative;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.letter-discovery-hud {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
background: rgba(255,255,255,0.1);
|
|
padding: 15px 20px;
|
|
border-radius: 15px;
|
|
backdrop-filter: blur(10px);
|
|
margin-bottom: 20px;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.hud-group {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 15px;
|
|
}
|
|
|
|
.hud-item {
|
|
color: white;
|
|
font-weight: bold;
|
|
font-size: 1.1em;
|
|
}
|
|
|
|
.phase-indicator {
|
|
background: rgba(255,255,255,0.2);
|
|
padding: 8px 16px;
|
|
border-radius: 20px;
|
|
font-size: 0.9em;
|
|
color: white;
|
|
backdrop-filter: blur(5px);
|
|
}
|
|
|
|
.letter-discovery-main {
|
|
background: rgba(255,255,255,0.1);
|
|
border-radius: 20px;
|
|
padding: 30px;
|
|
backdrop-filter: blur(10px);
|
|
min-height: 70vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.game-content {
|
|
width: 100%;
|
|
max-width: 900px;
|
|
text-align: center;
|
|
}
|
|
|
|
/* Letter Display Styles */
|
|
.letter-card {
|
|
background: rgba(255,255,255,0.95);
|
|
border-radius: 25px;
|
|
padding: 60px 40px;
|
|
margin: 30px auto;
|
|
max-width: 400px;
|
|
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
|
transform: scale(0.8);
|
|
animation: letterAppear 0.8s ease-out forwards;
|
|
}
|
|
|
|
@keyframes letterAppear {
|
|
to { transform: scale(1); }
|
|
}
|
|
|
|
.letter-display {
|
|
font-size: 8em;
|
|
font-weight: bold;
|
|
color: #667eea;
|
|
margin-bottom: 20px;
|
|
text-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
font-family: 'Arial Black', Arial, sans-serif;
|
|
}
|
|
|
|
.letter-info {
|
|
font-size: 1.5em;
|
|
color: #333;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.letter-pronunciation {
|
|
font-size: 1.2em;
|
|
color: #666;
|
|
font-style: italic;
|
|
margin-bottom: 25px;
|
|
}
|
|
|
|
.letter-controls {
|
|
display: flex;
|
|
gap: 15px;
|
|
justify-content: center;
|
|
margin-top: 30px;
|
|
}
|
|
|
|
/* Word Exploration Styles */
|
|
.word-exploration-header {
|
|
background: rgba(255,255,255,0.1);
|
|
padding: 20px;
|
|
border-radius: 15px;
|
|
margin-bottom: 30px;
|
|
backdrop-filter: blur(5px);
|
|
}
|
|
|
|
.exploring-letter {
|
|
font-size: 3em;
|
|
color: white;
|
|
margin-bottom: 10px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.word-progress {
|
|
color: rgba(255,255,255,0.8);
|
|
font-size: 1.1em;
|
|
}
|
|
|
|
.word-card {
|
|
background: rgba(255,255,255,0.95);
|
|
border-radius: 20px;
|
|
padding: 40px 30px;
|
|
margin: 25px auto;
|
|
max-width: 500px;
|
|
box-shadow: 0 15px 30px rgba(0,0,0,0.1);
|
|
transform: translateY(20px);
|
|
animation: wordSlideIn 0.6s ease-out forwards;
|
|
}
|
|
|
|
@keyframes wordSlideIn {
|
|
to { transform: translateY(0); }
|
|
}
|
|
|
|
.word-text {
|
|
font-size: 2.5em;
|
|
color: #667eea;
|
|
margin-bottom: 15px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.word-translation {
|
|
font-size: 1.3em;
|
|
color: #333;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.word-pronunciation {
|
|
font-size: 1.1em;
|
|
color: #666;
|
|
font-style: italic;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.word-type {
|
|
font-size: 0.9em;
|
|
color: #667eea;
|
|
background: rgba(102, 126, 234, 0.1);
|
|
padding: 4px 12px;
|
|
border-radius: 15px;
|
|
display: inline-block;
|
|
margin-bottom: 15px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.word-example {
|
|
font-size: 1em;
|
|
color: #555;
|
|
font-style: italic;
|
|
padding: 10px 15px;
|
|
background: rgba(0, 0, 0, 0.05);
|
|
border-left: 3px solid #667eea;
|
|
border-radius: 0 8px 8px 0;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
/* Practice Challenge Styles */
|
|
.practice-challenge {
|
|
text-align: center;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.challenge-text {
|
|
font-size: 1.8em;
|
|
color: white;
|
|
margin-bottom: 25px;
|
|
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
|
}
|
|
|
|
.practice-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 15px;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.practice-option {
|
|
background: rgba(255,255,255,0.9);
|
|
border: none;
|
|
border-radius: 15px;
|
|
padding: 20px;
|
|
font-size: 1.2em;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
color: #333;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.practice-option:hover {
|
|
background: rgba(255,255,255,1);
|
|
transform: translateY(-3px);
|
|
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
|
|
}
|
|
|
|
.practice-option.correct {
|
|
background: #4CAF50;
|
|
color: white;
|
|
animation: correctPulse 0.6s ease;
|
|
}
|
|
|
|
.practice-option.incorrect {
|
|
background: #F44336;
|
|
color: white;
|
|
animation: incorrectShake 0.6s ease;
|
|
}
|
|
|
|
@keyframes correctPulse {
|
|
0%, 100% { transform: scale(1); }
|
|
50% { transform: scale(1.05); }
|
|
}
|
|
|
|
@keyframes incorrectShake {
|
|
0%, 100% { transform: translateX(0); }
|
|
25% { transform: translateX(-5px); }
|
|
75% { transform: translateX(5px); }
|
|
}
|
|
|
|
.practice-stats {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
margin-top: 20px;
|
|
color: white;
|
|
font-size: 1.1em;
|
|
}
|
|
|
|
.stat-item {
|
|
text-align: center;
|
|
padding: 10px;
|
|
background: rgba(255,255,255,0.1);
|
|
border-radius: 10px;
|
|
backdrop-filter: blur(5px);
|
|
}
|
|
|
|
/* Control Buttons */
|
|
.discovery-btn {
|
|
background: linear-gradient(45deg, #667eea, #764ba2);
|
|
color: white;
|
|
border: none;
|
|
padding: 15px 30px;
|
|
border-radius: 25px;
|
|
font-size: 1.1em;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
margin: 0 10px;
|
|
}
|
|
|
|
.discovery-btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 20px rgba(0,0,0,0.3);
|
|
}
|
|
|
|
.discovery-btn:active {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.audio-btn {
|
|
background: none;
|
|
border: none;
|
|
font-size: 2em;
|
|
cursor: pointer;
|
|
color: #667eea;
|
|
margin-left: 15px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.audio-btn:hover {
|
|
transform: scale(1.2);
|
|
color: #764ba2;
|
|
}
|
|
|
|
/* Completion Message */
|
|
.completion-message {
|
|
text-align: center;
|
|
padding: 40px;
|
|
background: rgba(255,255,255,0.1);
|
|
border-radius: 20px;
|
|
backdrop-filter: blur(10px);
|
|
color: white;
|
|
}
|
|
|
|
.completion-title {
|
|
font-size: 2.5em;
|
|
margin-bottom: 20px;
|
|
color: #00ff88;
|
|
text-shadow: 0 2px 10px rgba(0,255,136,0.3);
|
|
}
|
|
|
|
.completion-stats {
|
|
font-size: 1.3em;
|
|
margin-bottom: 30px;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* Responsive Design */
|
|
@media (max-width: 768px) {
|
|
.letter-discovery-wrapper {
|
|
padding: 15px;
|
|
}
|
|
|
|
.letter-display {
|
|
font-size: 6em;
|
|
}
|
|
|
|
.word-text {
|
|
font-size: 2em;
|
|
}
|
|
|
|
.challenge-text {
|
|
font-size: 1.5em;
|
|
}
|
|
|
|
.practice-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
`;
|
|
document.head.appendChild(styleSheet);
|
|
}
|
|
|
|
extractContent() {
|
|
logSh('🔍 Letter Discovery - Extracting content...', 'INFO');
|
|
|
|
// Check for letters in content or rawContent
|
|
const letters = this.content.letters || this.content.rawContent?.letters;
|
|
|
|
if (letters && Object.keys(letters).length > 0) {
|
|
this.letters = Object.keys(letters).sort();
|
|
this.letterWords = letters;
|
|
logSh(`📝 Found ${this.letters.length} letters with words`, 'INFO');
|
|
} else {
|
|
this.showNoLettersMessage();
|
|
return;
|
|
}
|
|
|
|
logSh(`🎯 Letter Discovery ready: ${this.letters.length} letters`, 'INFO');
|
|
}
|
|
|
|
showNoLettersMessage() {
|
|
this.container.innerHTML = `
|
|
<div class="game-error">
|
|
<div class="error-content">
|
|
<h2>🔤 Letter Discovery</h2>
|
|
<p>❌ No letter structure found in this content.</p>
|
|
<p>This game requires content with a predefined letters system.</p>
|
|
<p>Try with content that includes letter-based learning material.</p>
|
|
<button class="back-btn" onclick="AppNavigation.navigateTo('games')">← Back to Games</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
init() {
|
|
this.container.innerHTML = `
|
|
<div class="letter-discovery-wrapper">
|
|
<div class="letter-discovery-hud">
|
|
<div class="hud-group">
|
|
<div class="hud-item">Score: <span id="score-display">${this.score}</span></div>
|
|
<div class="hud-item">Lives: <span id="lives-display">${this.lives}</span></div>
|
|
</div>
|
|
<div class="phase-indicator" id="phase-indicator">Letter Discovery</div>
|
|
<div class="hud-group">
|
|
<div class="hud-item">Progress: <span id="progress-display">0/${this.letters.length}</span></div>
|
|
</div>
|
|
</div>
|
|
<div class="letter-discovery-main">
|
|
<div class="game-content" id="game-content">
|
|
<!-- Dynamic content here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
this.updateHUD();
|
|
}
|
|
|
|
start() {
|
|
this.showLetterCard();
|
|
}
|
|
|
|
updateHUD() {
|
|
const scoreDisplay = document.getElementById('score-display');
|
|
const livesDisplay = document.getElementById('lives-display');
|
|
const progressDisplay = document.getElementById('progress-display');
|
|
const phaseIndicator = document.getElementById('phase-indicator');
|
|
|
|
if (scoreDisplay) scoreDisplay.textContent = this.score;
|
|
if (livesDisplay) livesDisplay.textContent = this.lives;
|
|
|
|
if (this.currentPhase === 'letter-discovery') {
|
|
if (progressDisplay) progressDisplay.textContent = `${this.currentLetterIndex}/${this.letters.length}`;
|
|
if (phaseIndicator) phaseIndicator.textContent = 'Letter Discovery';
|
|
} else if (this.currentPhase === 'word-exploration') {
|
|
if (progressDisplay) progressDisplay.textContent = `${this.currentWordIndex}/${this.letterWords[this.currentLetter].length}`;
|
|
if (phaseIndicator) phaseIndicator.textContent = `Exploring Letter "${this.currentLetter}"`;
|
|
} else if (this.currentPhase === 'practice') {
|
|
if (progressDisplay) progressDisplay.textContent = `Round ${this.practiceRound + 1}/${this.maxPracticeRounds}`;
|
|
if (phaseIndicator) phaseIndicator.textContent = `Practice - Level ${this.practiceLevel}`;
|
|
}
|
|
}
|
|
|
|
showLetterCard() {
|
|
if (this.currentLetterIndex >= this.letters.length) {
|
|
this.showCompletion();
|
|
return;
|
|
}
|
|
|
|
const letter = this.letters[this.currentLetterIndex];
|
|
const gameContent = document.getElementById('game-content');
|
|
|
|
gameContent.innerHTML = `
|
|
<div class="letter-card">
|
|
<div class="letter-display">${letter}</div>
|
|
<div class="letter-info">Letter "${letter}"</div>
|
|
<div class="letter-pronunciation">${this.getLetterPronunciation(letter)}</div>
|
|
<div class="letter-controls">
|
|
<button class="discovery-btn" onclick="window.currentLetterGame.discoverLetter()">
|
|
🔍 Discover Letter
|
|
</button>
|
|
<button class="audio-btn" onclick="window.currentLetterGame.playLetterSound('${letter}')">
|
|
🔊
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Store reference for button callbacks
|
|
window.currentLetterGame = this;
|
|
|
|
// Auto-play letter sound
|
|
setTimeout(() => this.playLetterSound(letter), 500);
|
|
}
|
|
|
|
getLetterPronunciation(letter) {
|
|
// Basic letter pronunciation guide
|
|
const pronunciations = {
|
|
'A': 'ay', 'B': 'bee', 'C': 'see', 'D': 'dee', 'E': 'ee',
|
|
'F': 'ef', 'G': 'gee', 'H': 'aych', 'I': 'eye', 'J': 'jay',
|
|
'K': 'kay', 'L': 'el', 'M': 'em', 'N': 'en', 'O': 'oh',
|
|
'P': 'pee', 'Q': 'cue', 'R': 'ar', 'S': 'ess', 'T': 'tee',
|
|
'U': 'you', 'V': 'vee', 'W': 'double-you', 'X': 'ex', 'Y': 'why', 'Z': 'zee'
|
|
};
|
|
return pronunciations[letter] || letter.toLowerCase();
|
|
}
|
|
|
|
playLetterSound(letter) {
|
|
if (window.SettingsManager && window.SettingsManager.speak) {
|
|
const speed = 0.8; // Slower for letters
|
|
window.SettingsManager.speak(letter, {
|
|
lang: this.content.language || 'en-US',
|
|
rate: speed
|
|
}).catch(error => {
|
|
console.warn('🔊 TTS failed for letter:', error);
|
|
});
|
|
}
|
|
}
|
|
|
|
discoverLetter() {
|
|
const letter = this.letters[this.currentLetterIndex];
|
|
this.discoveredLetters.push(letter);
|
|
this.score += 10;
|
|
this.onScoreUpdate(this.score);
|
|
|
|
// Start word exploration for this letter
|
|
this.currentLetter = letter;
|
|
this.currentPhase = 'word-exploration';
|
|
this.currentWordIndex = 0;
|
|
|
|
this.updateHUD();
|
|
this.showWordExploration();
|
|
}
|
|
|
|
showWordExploration() {
|
|
const words = this.letterWords[this.currentLetter];
|
|
|
|
if (!words || this.currentWordIndex >= words.length) {
|
|
// Finished exploring words for this letter
|
|
this.currentPhase = 'letter-discovery';
|
|
this.currentLetterIndex++;
|
|
this.updateHUD();
|
|
this.showLetterCard();
|
|
return;
|
|
}
|
|
|
|
const word = words[this.currentWordIndex];
|
|
const gameContent = document.getElementById('game-content');
|
|
|
|
gameContent.innerHTML = `
|
|
<div class="word-exploration-header">
|
|
<div class="exploring-letter">Letter "${this.currentLetter}"</div>
|
|
<div class="word-progress">Word ${this.currentWordIndex + 1} of ${words.length}</div>
|
|
</div>
|
|
<div class="word-card">
|
|
<div class="word-text">${word.word}</div>
|
|
<div class="word-translation">${word.translation}</div>
|
|
${word.pronunciation ? `<div class="word-pronunciation">[${word.pronunciation}]</div>` : ''}
|
|
${word.type ? `<div class="word-type">${word.type}</div>` : ''}
|
|
${word.example ? `<div class="word-example">"${word.example}"</div>` : ''}
|
|
<div class="letter-controls">
|
|
<button class="discovery-btn" onclick="window.currentLetterGame.nextWord()">
|
|
➡️ Next Word
|
|
</button>
|
|
<button class="audio-btn" onclick="window.currentLetterGame.playWordSound('${word.word}')">
|
|
🔊
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Add word to discovered list
|
|
this.discoveredWords.push(word);
|
|
|
|
// Auto-play word sound
|
|
setTimeout(() => this.playWordSound(word.word), 500);
|
|
}
|
|
|
|
playWordSound(word) {
|
|
if (window.SettingsManager && window.SettingsManager.speak) {
|
|
const speed = 0.9;
|
|
window.SettingsManager.speak(word, {
|
|
lang: this.content.language || 'en-US',
|
|
rate: speed
|
|
}).catch(error => {
|
|
console.warn('🔊 TTS failed for word:', error);
|
|
});
|
|
}
|
|
}
|
|
|
|
nextWord() {
|
|
this.currentWordIndex++;
|
|
this.score += 5;
|
|
this.onScoreUpdate(this.score);
|
|
this.updateHUD();
|
|
this.showWordExploration();
|
|
}
|
|
|
|
showCompletion() {
|
|
const gameContent = document.getElementById('game-content');
|
|
const totalWords = Object.values(this.letterWords).reduce((sum, words) => sum + words.length, 0);
|
|
|
|
gameContent.innerHTML = `
|
|
<div class="completion-message">
|
|
<div class="completion-title">🎉 All Letters Discovered!</div>
|
|
<div class="completion-stats">
|
|
Letters Discovered: ${this.discoveredLetters.length}<br>
|
|
Words Learned: ${this.discoveredWords.length}<br>
|
|
Final Score: ${this.score}
|
|
</div>
|
|
<div class="letter-controls">
|
|
<button class="discovery-btn" onclick="window.currentLetterGame.startPractice()">
|
|
🎮 Start Practice
|
|
</button>
|
|
<button class="discovery-btn" onclick="window.currentLetterGame.restart()">
|
|
🔄 Play Again
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
startPractice() {
|
|
this.currentPhase = 'practice';
|
|
this.practiceLevel = 1;
|
|
this.practiceRound = 0;
|
|
this.practiceCorrectAnswers = 0;
|
|
this.practiceErrors = 0;
|
|
|
|
// Create mixed practice from all discovered words
|
|
this.currentPracticeItems = this.shuffleArray([...this.discoveredWords]);
|
|
|
|
this.updateHUD();
|
|
this.showPracticeChallenge();
|
|
}
|
|
|
|
showPracticeChallenge() {
|
|
if (this.practiceRound >= this.maxPracticeRounds) {
|
|
this.endPractice();
|
|
return;
|
|
}
|
|
|
|
const currentItem = this.currentPracticeItems[this.practiceRound % this.currentPracticeItems.length];
|
|
const gameContent = document.getElementById('game-content');
|
|
|
|
// Generate options (correct + 3 random)
|
|
const allWords = this.discoveredWords.filter(w => w.word !== currentItem.word);
|
|
const randomOptions = this.shuffleArray([...allWords]).slice(0, 3);
|
|
const options = this.shuffleArray([currentItem, ...randomOptions]);
|
|
|
|
gameContent.innerHTML = `
|
|
<div class="practice-challenge">
|
|
<div class="challenge-text">What does "${currentItem.word}" mean?</div>
|
|
<div class="practice-grid">
|
|
${options.map((option, index) => `
|
|
<button class="practice-option" onclick="window.currentLetterGame.selectPracticeAnswer(${index}, '${option.word}')">
|
|
${option.translation}
|
|
</button>
|
|
`).join('')}
|
|
</div>
|
|
<div class="practice-stats">
|
|
<div class="stat-item">Correct: ${this.practiceCorrectAnswers}</div>
|
|
<div class="stat-item">Errors: ${this.practiceErrors}</div>
|
|
<div class="stat-item">Round: ${this.practiceRound + 1}/${this.maxPracticeRounds}</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Store correct answer for checking
|
|
this.currentCorrectAnswer = currentItem.word;
|
|
|
|
// Auto-play word
|
|
setTimeout(() => this.playWordSound(currentItem.word), 500);
|
|
}
|
|
|
|
selectPracticeAnswer(selectedIndex, selectedWord) {
|
|
const buttons = document.querySelectorAll('.practice-option');
|
|
const isCorrect = selectedWord === this.currentCorrectAnswer;
|
|
|
|
if (isCorrect) {
|
|
buttons[selectedIndex].classList.add('correct');
|
|
this.practiceCorrectAnswers++;
|
|
this.score += 10;
|
|
this.onScoreUpdate(this.score);
|
|
} else {
|
|
buttons[selectedIndex].classList.add('incorrect');
|
|
this.practiceErrors++;
|
|
// Show correct answer
|
|
buttons.forEach((btn, index) => {
|
|
if (btn.textContent.trim() === this.discoveredWords.find(w => w.word === this.currentCorrectAnswer)?.translation) {
|
|
btn.classList.add('correct');
|
|
}
|
|
});
|
|
}
|
|
|
|
setTimeout(() => {
|
|
this.practiceRound++;
|
|
this.updateHUD();
|
|
this.showPracticeChallenge();
|
|
}, 1500);
|
|
}
|
|
|
|
endPractice() {
|
|
const accuracy = Math.round((this.practiceCorrectAnswers / this.maxPracticeRounds) * 100);
|
|
const gameContent = document.getElementById('game-content');
|
|
|
|
gameContent.innerHTML = `
|
|
<div class="completion-message">
|
|
<div class="completion-title">🏆 Practice Complete!</div>
|
|
<div class="completion-stats">
|
|
Accuracy: ${accuracy}%<br>
|
|
Correct Answers: ${this.practiceCorrectAnswers}/${this.maxPracticeRounds}<br>
|
|
Final Score: ${this.score}
|
|
</div>
|
|
<div class="letter-controls">
|
|
<button class="discovery-btn" onclick="window.currentLetterGame.restart()">
|
|
🔄 Play Again
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// End game
|
|
setTimeout(() => {
|
|
this.onGameEnd(this.score);
|
|
}, 3000);
|
|
}
|
|
|
|
shuffleArray(array) {
|
|
const shuffled = [...array];
|
|
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
const j = Math.floor(Math.random() * (i + 1));
|
|
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
}
|
|
return shuffled;
|
|
}
|
|
|
|
restart() {
|
|
this.currentPhase = 'letter-discovery';
|
|
this.currentLetterIndex = 0;
|
|
this.discoveredLetters = [];
|
|
this.currentLetter = null;
|
|
this.currentWordIndex = 0;
|
|
this.discoveredWords = [];
|
|
this.score = 0;
|
|
this.lives = 3;
|
|
this.practiceLevel = 1;
|
|
this.practiceRound = 0;
|
|
this.practiceCorrectAnswers = 0;
|
|
this.practiceErrors = 0;
|
|
this.currentPracticeItems = [];
|
|
|
|
this.updateHUD();
|
|
this.start();
|
|
}
|
|
|
|
destroy() {
|
|
// Cleanup
|
|
if (window.currentLetterGame === this) {
|
|
delete window.currentLetterGame;
|
|
}
|
|
|
|
const styleSheet = document.getElementById('letter-discovery-styles');
|
|
if (styleSheet) {
|
|
styleSheet.remove();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Register the game module
|
|
window.GameModules = window.GameModules || {};
|
|
window.GameModules.LetterDiscovery = LetterDiscovery; |