736 lines
23 KiB
JavaScript
736 lines
23 KiB
JavaScript
// === MODULE JEUX TEMPORAIRES ===
|
|
|
|
class TempGamesModule {
|
|
constructor(options) {
|
|
this.container = options.container;
|
|
this.content = options.content;
|
|
this.onScoreUpdate = options.onScoreUpdate || (() => {});
|
|
this.onGameEnd = options.onGameEnd || (() => {});
|
|
|
|
this.currentGame = null;
|
|
this.availableGames = [
|
|
{
|
|
id: 'word-match',
|
|
name: 'Word Match',
|
|
icon: '🎯',
|
|
description: 'Associe les mots anglais avec leur traduction',
|
|
difficulty: 'easy'
|
|
},
|
|
{
|
|
id: 'quick-translation',
|
|
name: 'Quick Translation',
|
|
icon: '⚡',
|
|
description: 'Traduis le mot le plus rapidement possible',
|
|
difficulty: 'medium'
|
|
},
|
|
{
|
|
id: 'word-builder',
|
|
name: 'Word Builder',
|
|
icon: '🔤',
|
|
description: 'Reconstitue le mot lettre par lettre',
|
|
difficulty: 'medium'
|
|
}
|
|
];
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
this.showGameSelector();
|
|
}
|
|
|
|
showGameSelector() {
|
|
this.container.innerHTML = `
|
|
<div class="temp-games-wrapper">
|
|
<div class="game-selector-header">
|
|
<h3>🎯 Mini-Jeux Temporaires</h3>
|
|
<p>Sélectionne un mini-jeu pour t'amuser avec le vocabulaire !</p>
|
|
</div>
|
|
|
|
<div class="mini-games-grid">
|
|
${this.availableGames.map(game => this.createGameCard(game)).join('')}
|
|
</div>
|
|
|
|
<div class="temp-games-info">
|
|
<p><em>Ces jeux sont en développement et seront bientôt des modules complets !</em></p>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
this.setupGameSelector();
|
|
}
|
|
|
|
createGameCard(game) {
|
|
const difficultyColor = {
|
|
easy: '#10B981',
|
|
medium: '#F59E0B',
|
|
hard: '#EF4444'
|
|
}[game.difficulty];
|
|
|
|
return `
|
|
<div class="mini-game-card" data-game="${game.id}">
|
|
<div class="mini-game-icon">${game.icon}</div>
|
|
<h4 class="mini-game-title">${game.name}</h4>
|
|
<p class="mini-game-description">${game.description}</p>
|
|
<div class="mini-game-difficulty" style="color: ${difficultyColor}">
|
|
${game.difficulty.toUpperCase()}
|
|
</div>
|
|
<button class="play-mini-game-btn">Jouer</button>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
setupGameSelector() {
|
|
document.querySelectorAll('.mini-game-card').forEach(card => {
|
|
card.addEventListener('click', () => {
|
|
const gameId = card.dataset.game;
|
|
this.startMiniGame(gameId);
|
|
});
|
|
});
|
|
}
|
|
|
|
startMiniGame(gameId) {
|
|
const game = this.availableGames.find(g => g.id === gameId);
|
|
if (!game) return;
|
|
|
|
switch(gameId) {
|
|
case 'word-match':
|
|
this.startWordMatch();
|
|
break;
|
|
case 'quick-translation':
|
|
this.startQuickTranslation();
|
|
break;
|
|
case 'word-builder':
|
|
this.startWordBuilder();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// === WORD MATCH GAME ===
|
|
startWordMatch() {
|
|
this.container.innerHTML = `
|
|
<div class="mini-game word-match-game">
|
|
<div class="mini-game-header">
|
|
<button class="back-to-selector">← Retour</button>
|
|
<h3>🎯 Word Match</h3>
|
|
<div class="mini-score">Score: <span id="match-score">0</span></div>
|
|
</div>
|
|
|
|
<div class="word-match-board">
|
|
<div class="english-words" id="english-words">
|
|
<!-- Mots anglais -->
|
|
</div>
|
|
<div class="french-words" id="french-words">
|
|
<!-- Mots français -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="match-feedback" id="match-feedback">
|
|
Clique sur un mot anglais, puis sur sa traduction française !
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
this.setupWordMatch();
|
|
}
|
|
|
|
setupWordMatch() {
|
|
document.querySelector('.back-to-selector').addEventListener('click', () => {
|
|
this.showGameSelector();
|
|
});
|
|
|
|
const words = this.content.vocabulary.slice(0, 6).map(w => ({
|
|
english: w.english,
|
|
french: w.french
|
|
}));
|
|
|
|
const shuffledFrench = [...words].sort(() => Math.random() - 0.5);
|
|
|
|
const englishContainer = document.getElementById('english-words');
|
|
const frenchContainer = document.getElementById('french-words');
|
|
|
|
let selectedEnglish = null;
|
|
let matchedPairs = 0;
|
|
let score = 0;
|
|
|
|
words.forEach((word, index) => {
|
|
const englishBtn = document.createElement('button');
|
|
englishBtn.className = 'word-btn english-btn';
|
|
englishBtn.textContent = word.english;
|
|
englishBtn.dataset.word = word.english;
|
|
englishContainer.appendChild(englishBtn);
|
|
|
|
englishBtn.addEventListener('click', () => {
|
|
document.querySelectorAll('.english-btn').forEach(btn =>
|
|
btn.classList.remove('selected'));
|
|
englishBtn.classList.add('selected');
|
|
selectedEnglish = word.english;
|
|
});
|
|
});
|
|
|
|
shuffledFrench.forEach(word => {
|
|
const frenchBtn = document.createElement('button');
|
|
frenchBtn.className = 'word-btn french-btn';
|
|
frenchBtn.textContent = word.french;
|
|
frenchBtn.dataset.word = word.french;
|
|
frenchContainer.appendChild(frenchBtn);
|
|
|
|
frenchBtn.addEventListener('click', () => {
|
|
if (!selectedEnglish) {
|
|
document.getElementById('match-feedback').textContent =
|
|
'Sélectionne d\'abord un mot anglais !';
|
|
return;
|
|
}
|
|
|
|
const correctWord = words.find(w => w.english === selectedEnglish);
|
|
if (correctWord && correctWord.french === word.french) {
|
|
// Correct match
|
|
score += 10;
|
|
matchedPairs++;
|
|
|
|
document.querySelector(`[data-word="${selectedEnglish}"]`).classList.add('matched');
|
|
frenchBtn.classList.add('matched');
|
|
|
|
document.getElementById('match-feedback').textContent = 'Parfait ! 🎉';
|
|
|
|
if (matchedPairs === words.length) {
|
|
setTimeout(() => {
|
|
alert(`Félicitations ! Score final: ${score}`);
|
|
this.onGameEnd(score);
|
|
}, 1000);
|
|
}
|
|
} else {
|
|
// Wrong match
|
|
score = Math.max(0, score - 2);
|
|
document.getElementById('match-feedback').textContent =
|
|
`Non, "${selectedEnglish}" ne correspond pas à "${word.french}"`;
|
|
}
|
|
|
|
document.getElementById('match-score').textContent = score;
|
|
this.onScoreUpdate(score);
|
|
selectedEnglish = null;
|
|
document.querySelectorAll('.english-btn').forEach(btn =>
|
|
btn.classList.remove('selected'));
|
|
});
|
|
});
|
|
}
|
|
|
|
// === QUICK TRANSLATION GAME ===
|
|
startQuickTranslation() {
|
|
this.container.innerHTML = `
|
|
<div class="mini-game quick-translation-game">
|
|
<div class="mini-game-header">
|
|
<button class="back-to-selector">← Retour</button>
|
|
<h3>⚡ Quick Translation</h3>
|
|
<div class="game-stats">
|
|
<span>Score: <span id="quick-score">0</span></span>
|
|
<span>Temps: <span id="quick-time">30</span>s</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="quick-translation-board">
|
|
<div class="word-to-translate">
|
|
<h2 id="current-word">---</h2>
|
|
<p>Traduction en français :</p>
|
|
</div>
|
|
|
|
<div class="translation-options" id="translation-options">
|
|
<!-- Options de traduction -->
|
|
</div>
|
|
</div>
|
|
|
|
<button id="start-quick-game" class="start-btn">🎮 Commencer</button>
|
|
</div>
|
|
`;
|
|
|
|
this.setupQuickTranslation();
|
|
}
|
|
|
|
setupQuickTranslation() {
|
|
document.querySelector('.back-to-selector').addEventListener('click', () => {
|
|
this.showGameSelector();
|
|
});
|
|
|
|
let currentWordIndex = 0;
|
|
let score = 0;
|
|
let timeLeft = 30;
|
|
let gameTimer = null;
|
|
let isPlaying = false;
|
|
|
|
const words = this.shuffleArray([...this.content.vocabulary]).slice(0, 10);
|
|
|
|
document.getElementById('start-quick-game').addEventListener('click', () => {
|
|
if (isPlaying) return;
|
|
|
|
isPlaying = true;
|
|
currentWordIndex = 0;
|
|
score = 0;
|
|
timeLeft = 30;
|
|
|
|
document.getElementById('start-quick-game').style.display = 'none';
|
|
|
|
gameTimer = setInterval(() => {
|
|
timeLeft--;
|
|
document.getElementById('quick-time').textContent = timeLeft;
|
|
|
|
if (timeLeft <= 0) {
|
|
clearInterval(gameTimer);
|
|
alert(`Temps écoulé ! Score final: ${score}`);
|
|
this.onGameEnd(score);
|
|
}
|
|
}, 1000);
|
|
|
|
this.showQuickWord();
|
|
});
|
|
|
|
const showQuickWord = () => {
|
|
if (currentWordIndex >= words.length) {
|
|
clearInterval(gameTimer);
|
|
alert(`Bravo ! Score final: ${score}`);
|
|
this.onGameEnd(score);
|
|
return;
|
|
}
|
|
|
|
const currentWord = words[currentWordIndex];
|
|
document.getElementById('current-word').textContent = currentWord.english;
|
|
|
|
// Créer 4 options (1 correcte + 3 fausses)
|
|
const options = [currentWord.french];
|
|
const otherWords = words.filter(w => w.french !== currentWord.french);
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
if (otherWords[i]) {
|
|
options.push(otherWords[i].french);
|
|
}
|
|
}
|
|
|
|
const shuffledOptions = this.shuffleArray(options);
|
|
const optionsContainer = document.getElementById('translation-options');
|
|
optionsContainer.innerHTML = '';
|
|
|
|
shuffledOptions.forEach(option => {
|
|
const btn = document.createElement('button');
|
|
btn.className = 'option-btn';
|
|
btn.textContent = option;
|
|
|
|
btn.addEventListener('click', () => {
|
|
if (option === currentWord.french) {
|
|
score += 5;
|
|
btn.classList.add('correct');
|
|
setTimeout(() => {
|
|
currentWordIndex++;
|
|
this.showQuickWord();
|
|
}, 500);
|
|
} else {
|
|
score = Math.max(0, score - 1);
|
|
btn.classList.add('wrong');
|
|
document.querySelector(`button:contains("${currentWord.french}")`);
|
|
}
|
|
|
|
document.getElementById('quick-score').textContent = score;
|
|
this.onScoreUpdate(score);
|
|
|
|
// Désactiver tous les boutons
|
|
document.querySelectorAll('.option-btn').forEach(b => b.disabled = true);
|
|
});
|
|
|
|
optionsContainer.appendChild(btn);
|
|
});
|
|
};
|
|
|
|
this.showQuickWord = showQuickWord;
|
|
}
|
|
|
|
// === WORD BUILDER GAME ===
|
|
startWordBuilder() {
|
|
this.container.innerHTML = `
|
|
<div class="mini-game word-builder-game">
|
|
<div class="mini-game-header">
|
|
<button class="back-to-selector">← Retour</button>
|
|
<h3>🔤 Word Builder</h3>
|
|
<div class="mini-score">Score: <span id="builder-score">0</span></div>
|
|
</div>
|
|
|
|
<div class="word-builder-board">
|
|
<div class="french-word">
|
|
<p>Traduction : <strong id="french-hint">---</strong></p>
|
|
</div>
|
|
|
|
<div class="word-construction">
|
|
<div class="word-spaces" id="word-spaces">
|
|
<!-- Espaces pour les lettres -->
|
|
</div>
|
|
|
|
<div class="available-letters" id="available-letters">
|
|
<!-- Lettres disponibles -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="builder-controls">
|
|
<button id="next-word-btn" style="display:none;">Mot suivant</button>
|
|
<button id="give-up-btn">Passer</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
this.setupWordBuilder();
|
|
}
|
|
|
|
setupWordBuilder() {
|
|
document.querySelector('.back-to-selector').addEventListener('click', () => {
|
|
this.showGameSelector();
|
|
});
|
|
|
|
let currentWordIndex = 0;
|
|
let score = 0;
|
|
const words = this.shuffleArray([...this.content.vocabulary]).slice(0, 8);
|
|
|
|
const showBuilderWord = () => {
|
|
if (currentWordIndex >= words.length) {
|
|
alert(`Félicitations ! Score final: ${score}`);
|
|
this.onGameEnd(score);
|
|
return;
|
|
}
|
|
|
|
const currentWord = words[currentWordIndex];
|
|
const wordSpaces = document.getElementById('word-spaces');
|
|
const availableLetters = document.getElementById('available-letters');
|
|
|
|
document.getElementById('french-hint').textContent = currentWord.french;
|
|
|
|
// Créer les espaces
|
|
wordSpaces.innerHTML = '';
|
|
currentWord.english.split('').forEach((letter, index) => {
|
|
const space = document.createElement('div');
|
|
space.className = 'letter-space';
|
|
space.dataset.index = index;
|
|
space.dataset.letter = letter.toLowerCase();
|
|
wordSpaces.appendChild(space);
|
|
});
|
|
|
|
// Créer les lettres mélangées + quelques lettres supplémentaires
|
|
const wordLetters = currentWord.english.toLowerCase().split('');
|
|
const extraLetters = 'abcdefghijklmnopqrstuvwxyz'.split('')
|
|
.filter(l => !wordLetters.includes(l))
|
|
.slice(0, 3);
|
|
|
|
const allLetters = this.shuffleArray([...wordLetters, ...extraLetters]);
|
|
|
|
availableLetters.innerHTML = '';
|
|
allLetters.forEach(letter => {
|
|
const letterBtn = document.createElement('button');
|
|
letterBtn.className = 'letter-btn';
|
|
letterBtn.textContent = letter.toUpperCase();
|
|
letterBtn.dataset.letter = letter;
|
|
|
|
letterBtn.addEventListener('click', () => {
|
|
this.placeLetter(letter, letterBtn);
|
|
});
|
|
|
|
availableLetters.appendChild(letterBtn);
|
|
});
|
|
|
|
document.getElementById('next-word-btn').style.display = 'none';
|
|
document.getElementById('give-up-btn').style.display = 'inline-block';
|
|
};
|
|
|
|
const placeLetter = (letter, btn) => {
|
|
const emptySpace = document.querySelector(
|
|
`.letter-space[data-letter="${letter}"]:not(.filled)`
|
|
);
|
|
|
|
if (emptySpace) {
|
|
emptySpace.textContent = letter.toUpperCase();
|
|
emptySpace.classList.add('filled');
|
|
btn.disabled = true;
|
|
|
|
// Vérifier si le mot est complet
|
|
const allSpaces = document.querySelectorAll('.letter-space');
|
|
const filledSpaces = document.querySelectorAll('.letter-space.filled');
|
|
|
|
if (allSpaces.length === filledSpaces.length) {
|
|
score += 15;
|
|
document.getElementById('builder-score').textContent = score;
|
|
this.onScoreUpdate(score);
|
|
|
|
document.getElementById('next-word-btn').style.display = 'inline-block';
|
|
document.getElementById('give-up-btn').style.display = 'none';
|
|
|
|
// Désactiver toutes les lettres
|
|
document.querySelectorAll('.letter-btn').forEach(b => b.disabled = true);
|
|
}
|
|
} else {
|
|
// Mauvaise lettre
|
|
btn.classList.add('wrong-letter');
|
|
setTimeout(() => btn.classList.remove('wrong-letter'), 1000);
|
|
}
|
|
};
|
|
|
|
document.getElementById('next-word-btn').addEventListener('click', () => {
|
|
currentWordIndex++;
|
|
showBuilderWord();
|
|
});
|
|
|
|
document.getElementById('give-up-btn').addEventListener('click', () => {
|
|
currentWordIndex++;
|
|
showBuilderWord();
|
|
});
|
|
|
|
this.placeLetter = placeLetter;
|
|
showBuilderWord();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
start() {
|
|
// Interface commune - montrer le sélecteur
|
|
this.showGameSelector();
|
|
}
|
|
|
|
destroy() {
|
|
this.container.innerHTML = '';
|
|
}
|
|
}
|
|
|
|
// CSS supplémentaire pour les mini-jeux
|
|
const tempGamesStyles = `
|
|
<style>
|
|
.temp-games-wrapper {
|
|
text-align: center;
|
|
}
|
|
|
|
.mini-games-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 20px;
|
|
margin: 30px 0;
|
|
}
|
|
|
|
.mini-game-card {
|
|
background: white;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 12px;
|
|
padding: 20px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.mini-game-card:hover {
|
|
border-color: var(--primary-color);
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.mini-game-icon {
|
|
font-size: 2.5rem;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.mini-game-title {
|
|
color: var(--primary-color);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.mini-game-description {
|
|
color: var(--text-secondary);
|
|
font-size: 0.9rem;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.mini-game-difficulty {
|
|
font-weight: bold;
|
|
font-size: 0.8rem;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.play-mini-game-btn {
|
|
background: var(--primary-color);
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 16px;
|
|
border-radius: 20px;
|
|
font-size: 0.9rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.mini-game {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.mini-game-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 30px;
|
|
padding: 20px;
|
|
background: white;
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.word-match-board {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 30px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.word-btn {
|
|
display: block;
|
|
width: 100%;
|
|
padding: 12px;
|
|
margin-bottom: 10px;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
background: white;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.word-btn:hover {
|
|
border-color: var(--primary-color);
|
|
}
|
|
|
|
.word-btn.selected {
|
|
background: var(--primary-color);
|
|
color: white;
|
|
border-color: var(--primary-color);
|
|
}
|
|
|
|
.word-btn.matched {
|
|
background: var(--secondary-color);
|
|
color: white;
|
|
border-color: var(--secondary-color);
|
|
cursor: default;
|
|
}
|
|
|
|
.translation-options {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 15px;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.option-btn {
|
|
padding: 15px;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
background: white;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.option-btn:hover {
|
|
border-color: var(--primary-color);
|
|
}
|
|
|
|
.option-btn.correct {
|
|
background: var(--secondary-color);
|
|
color: white;
|
|
border-color: var(--secondary-color);
|
|
}
|
|
|
|
.option-btn.wrong {
|
|
background: var(--error-color);
|
|
color: white;
|
|
border-color: var(--error-color);
|
|
}
|
|
|
|
.word-construction {
|
|
margin: 30px 0;
|
|
}
|
|
|
|
.word-spaces {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.letter-space {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.2rem;
|
|
font-weight: bold;
|
|
background: white;
|
|
}
|
|
|
|
.letter-space.filled {
|
|
background: var(--secondary-color);
|
|
color: white;
|
|
border-color: var(--secondary-color);
|
|
}
|
|
|
|
.available-letters {
|
|
display: flex;
|
|
justify-content: center;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
|
|
.letter-btn {
|
|
width: 35px;
|
|
height: 35px;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
background: white;
|
|
cursor: pointer;
|
|
font-size: 1rem;
|
|
font-weight: bold;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.letter-btn:hover {
|
|
border-color: var(--primary-color);
|
|
}
|
|
|
|
.letter-btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.letter-btn.wrong-letter {
|
|
background: var(--error-color);
|
|
color: white;
|
|
border-color: var(--error-color);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.word-match-board {
|
|
grid-template-columns: 1fr;
|
|
gap: 20px;
|
|
}
|
|
|
|
.translation-options {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.mini-game-header {
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
}
|
|
</style>
|
|
`;
|
|
|
|
// Ajouter les styles
|
|
document.head.insertAdjacentHTML('beforeend', tempGamesStyles);
|
|
|
|
// Enregistrement du module
|
|
window.GameModules = window.GameModules || {};
|
|
window.GameModules.TempGames = TempGamesModule; |