saeidBahrami.ir
const lessonData = { unit1: [ { name: "Cake", nameFa: "کیک", id: "cake", emoji: "🍰" }, { name: "Candy", nameFa: "آب نبات", id: "candy", emoji: "🍬" }, { name: "Popcorn", nameFa: "پاپ کورن", id: "popcorn", emoji: "🍿" }, { name: "Apple", nameFa: "سیب", id: "apple", emoji: "🍎" }, { name: "Chips", nameFa: "چیپس", id: "chips", emoji: "🍟" }, { name: "Cookies", nameFa: "کلوچه", id: "cookies", emoji: "🍪" }, { name: "Sandwiches", nameFa: "ساندویچ", id: "sandwiches", emoji: "🥪" } ], unit2: [ { name: "Cricket", nameFa: "جیرجیرک", id: "cricket", emoji: "🦗" }, { name: "Bee", nameFa: "زنبور", id: "bee", emoji: "🐝" }, { name: "Ant", nameFa: "مورچه", id: "ant", emoji: "🐜" }, { name: "Spider", nameFa: "عنکبوت", id: "spider", emoji: "🕷️" }, { name: "Worm", nameFa: "کرم", id: "worm", emoji: "🪱" }, { name: "Ladybug", nameFa: "کفش‌دوزک", id: "ladybug", emoji: "🐞" }, { name: "Butterfly", nameFa: "پروانه", id: "butterfly", emoji: "🦋" }, ], unit3: [ { name: "Banana", nameFa: "موز", id: "banana", emoji: "🍌" }, { name: "Peach", nameFa: "هلو", id: "peach", emoji: "🍑" }, { name: "Pear", nameFa: "گلابی", id: "pear", emoji: "🍐" }, { name: "Orange", nameFa: "پرتقال", id: "orange", emoji: "🍊" }, { name: "Coconut", nameFa: "نارگیل", id: "coconut", emoji: "🥥" }, { name: "Pineapple", nameFa: "آناناس", id: "pineapple", emoji: "🍍" }, { name: "Grapes", nameFa: "انگور", id: "grapes", emoji: "🍇" }, { name: "Strawberries", nameFa: "توت فرنگی", id: "strawberries", emoji: "🍓" } ], unit4: [ { name: "Tree", nameFa: "درخت", id: "tree", emoji: "🌳" }, { name: "Grass", nameFa: "چمن", id: "grass", emoji: "🌱" }, { name: "Sun", nameFa: "خورشید", id: "sun", emoji: "☀️" }, { name: "Cloud", nameFa: "ابر", id: "cloud", emoji: "☁️" }, { name: "Sky", nameFa: "آسمان", id: "sky", emoji: "🌌" }, { name: "Door", nameFa: "در", id: "door", emoji: "🚪" }, { name: "Window", nameFa: "پنجره", id: "window", emoji: "🖼️" }, { name: "House", nameFa: "خانه", id: "house", emoji: "🏠" }, { name: "Flower", nameFa: "گل", id: "flower", emoji: "🌸" } ], unit5: [ { name: "Head", nameFa: "سر", id: "head", emoji: "🙆" }, { name: "Ears", nameFa: "گوش ها", id: "ears", emoji: "👂" }, { name: "Eyes", nameFa: "چشم ها", id: "eyes", emoji: "👀" }, { name: "Nose", nameFa: "بینی", id: "nose", emoji: "👃" }, { name: "Mouth", nameFa: "دهان", id: "mouth", emoji: "👄" }, { name: "Arm", nameFa: "بازو", id: "arm", emoji: "💪" }, { name: "Hand", nameFa: "دست", id: "hand", emoji: "✋" }, { name: "Leg", nameFa: "پا", id: "leg", emoji: "🦵" }, { name: "Foot", nameFa: "کف پا", id: "foot", emoji: "🦶" } ], unit6: [ { name: "Mommy", nameFa: "مامان", id: "mommy", emoji: "👩‍👧" }, { name: "Daddy", nameFa: "بابا", id: "daddy", emoji: "👨‍👦" }, { name: "Brother", nameFa: "برادر", id: "brother", emoji: "👦" }, { name: "Sister", nameFa: "خواهر", id: "sister", emoji: "👧" }, { name: "Baby sister", nameFa: "خواهر کوچک", id: "babysister", emoji: "👶" }, // id تغییر داده شد { name: "Grandpa", nameFa: "پدربزرگ", id: "grandpa", emoji: "👴" }, { name: "Grandma", nameFa: "مادربزرگ", id: "grandma", emoji: "👵" }, { name: "Family", nameFa: "خانواده", id: "family", emoji: "👨‍👩‍👧‍👦" } ], unit7: [ { name: "Girl", nameFa: "دختر", id: "girl", emoji: "👧" }, { name: "Paper", nameFa: "کاغذ", id: "paper", emoji: "📄" }, { name: "Pencil", nameFa: "مداد", id: "pencil", emoji: "✏️" }, { name: "Scissors", nameFa: "قیچی", id: "scissors", emoji: "✂️" }, { name: "Glue", nameFa: "چسب", id: "glue", emoji: "🩹" }, { name: "Teacher", nameFa: "معلم", id: "teacher", emoji: "👩‍🏫" }, { name: "Notebook", nameFa: "دفتر", id: "notebook", emoji: "📓" }, { name: "Boy", nameFa: "پسر", id: "boy", emoji: "👦" }, { name: "Pen", nameFa: "خودکار", id: "pen", emoji: "🖊️" } ], unit8: [ { name: "Brown", nameFa: "قهوه ای", id: "brown", emoji: "🟤" }, { name: "Orange", nameFa: "نارنجی", id: "orange", emoji: "🟠" }, { name: "Green", nameFa: "سبز", id: "green", emoji: "🟢" }, { name: "Purple", nameFa: "بنفش", id: "purple", emoji: "🟣" }, { name: "Heart", nameFa: "قلب", id: "heart", emoji: "❤️" }, { name: "Square", nameFa: "مربع", id: "square", emoji: "⬛" }, { name: "Circle", nameFa: "دایره", id: "circle", emoji: "⚫" }, { name: "Triangle", nameFa: "مثلث", id: "triangle", emoji: "🔺" }, { name: "Rectangle", nameFa: "مستطیل", id: "rectangle", emoji: "▭" } // یک شکل غیررسمی برای مستطیل ], unit9: [ { name: "Marker", nameFa: "ماژیک", id: "marker", emoji: "🖍️" }, { name: "Paint", nameFa: "رنگ", id: "paint", emoji: "🎨" }, { name: "Brush", nameFa: "قلم مو", id: "brush", emoji: "🖌️" }, { name: "Red", nameFa: "قرمز", id: "red", emoji: "🔴" }, { name: "Yellow", nameFa: "زرد", id: "yellow", emoji: "🟡" }, { name: "Blue", nameFa: "آبی", id: "blue", emoji: "🔵" }, { name: "White", nameFa: "سفید", id: "white", emoji: "⚪" }, { name: "Black", nameFa: "سیاه", id: "black", emoji: "⚫" } ], unit10: [ { name: "Kite", nameFa: "بادبادک", id: "kite", emoji: "🪁" }, { name: "Ball", nameFa: "توپ", id: "ball", emoji: "⚽" }, // ایموجی چرخ و فلک به دلیل عدم وجود ایموجی مستقیم تاب ] }; function loadBestScore() { bestScore = parseInt(localStorage.getItem("bestScore") || "0"); } function saveBestScore() { if (score > bestScore) { bestScore = score; localStorage.setItem("bestScore", bestScore.toString()); } } function checkDevice() { isMobile = window.innerWidth <= 768; const canvas = document.getElementById("gameCanvas"); if (document.getElementById('game-container').style.display !== 'none') { if (isMobile) { canvasWidth = Math.min(window.innerWidth - 20, 400); canvasHeight = Math.min(window.innerHeight - 150, 600); } else { canvasWidth = Math.min(window.innerWidth - 40, 800); canvasHeight = Math.min(window.innerHeight - 150, 600); } canvas.width = canvasWidth; canvas.height = canvasHeight; } } function init() { loadBestScore(); document.getElementById('lesson-select').addEventListener('change', function() { document.getElementById('start-game-btn').disabled = this.value === ""; }); document.getElementById('start-game-btn').addEventListener('click', initializeLesson); window.addEventListener('resize', () => { checkDevice(); if (stage && document.getElementById('game-container').style.display !== 'none') { setupUI(); if (gameObjects.length > 0) positionWords(); } }); stage = new createjs.Stage("gameCanvas"); createjs.Touch.enable(stage); createjs.Ticker.framerate = 60; createjs.Ticker.addEventListener("tick", stage); const manifest = [ {src: "sounds/correct.mp3", id: "correct_sound"}, {src: "https://actions.google.com/sounds/v1/cartoon/wood_plank_flicks.ogg", id: "incorrect_sound"}, {src: "https://actions.google.com/sounds/v1/cartoon/cartoon_boing.ogg", id: "gameover_sound"} ]; queue = new createjs.LoadQueue(false); queue.installPlugin(createjs.Sound); queue.loadManifest(manifest); queue.on("error", function(e){ console.warn("LoadQueue error:", e); }); } // ===== منطق لایتنر سیستم ===== function getNextWords(numChoices = 4) { let now = new Date(); let progress = JSON.parse(localStorage.getItem("progress") || "{}"); let dueWords = currentWords.filter(word => { let record = progress[word.id]; return !record || new Date(record.nextReview) <= now; }); if (dueWords.length === 0) dueWords = currentWords.slice(); let correct = dueWords[Math.floor(Math.random() * dueWords.length)]; let others = currentWords.filter(word => word.id !== correct.id) .sort(() => 0.5 - Math.random()) .slice(0, numChoices - 1); return [correct, ...others].sort(() => 0.5 - Math.random()); } function updateLeitner(wordId, isCorrect) { let progress = JSON.parse(localStorage.getItem("progress") || "{}"); let record = progress[wordId] || { box: 1 }; if (isCorrect) record.box = Math.min(record.box + 1, 5); else record.box = 1; const intervals = [0, 1, 2, 4, 7, 15]; let next = new Date(); next.setDate(next.getDate() + intervals[record.box]); record.nextReview = next.toISOString(); progress[wordId] = record; localStorage.setItem("progress", JSON.stringify(progress)); } function initializeLesson() { const selectedLesson = document.getElementById('lesson-select').value; if (selectedLesson && lessonData[selectedLesson]) { currentWords = lessonData[selectedLesson]; document.getElementById('main-menu').style.display = 'none'; document.getElementById('game-container').style.display = 'flex'; checkDevice(); startGame(); } } function returnToMenu() { clearInterval(timerInterval); stage.removeAllChildren(); gameObjects = []; document.getElementById('game-container').style.display = 'none'; document.getElementById('main-menu').style.display = 'flex'; document.getElementById('lesson-select').value = ""; document.getElementById('start-game-btn').disabled = true; } // ===== منطق بازی ===== function startGame() { score = 0; lives = 3; currentRound = 1; streakCount = 0; setupUI(); nextRound(); } function setupUI() { stage.removeAllChildren(); homeButton = createHomeButton(returnToMenu); homeButton.x = 20; homeButton.y = 15; scoreText = new createjs.Text(`امتیاز: ${score}`, "bold 20px Arial", "#fff"); scoreText.textAlign = "center"; scoreText.x = canvasWidth / 2; scoreText.y = 20; scoreText.shadow = new createjs.Shadow("#000", 2, 2, 5); livesText = new createjs.Text(`${'❤️'.repeat(lives)}`, "24px Arial", "#e74c3c"); livesText.textAlign = "right"; livesText.x = canvasWidth - 20; livesText.y = 20; timerContainerShape = new createjs.Shape(); timerContainerShape.graphics.beginFill("rgba(0,0,0,0.15)").drawRoundRect(0,0,canvasWidth - 40,10,5); timerContainerShape.x = 20; timerContainerShape.y = 60; timerBarShape = new createjs.Shape(); timerBarShape.x = 20; timerBarShape.y = 60; timerBarShape.graphics.beginFill("#4CAF50").drawRoundRect(0,0,canvasWidth - 40,10,5); questionText = new createjs.Text("", "bold 28px Arial", "#2c3e50"); questionText.textAlign = "center"; questionText.x = canvasWidth / 2; questionText.y = 90; questionText.shadow = new createjs.Shadow("rgba(0,0,0,0.1)", 1,1,3); const speakerBg = new createjs.Shape(); speakerBg.graphics.beginLinearGradientFill(["#3498db","#2980b9"], [0,1], 0,0,0,60).drawRoundRect(0,0,60,60,30); const speakerIcon = new createjs.Text("🔊", "30px Arial", "#fff"); speakerIcon.textAlign = "center"; speakerIcon.textBaseline = "middle"; speakerIcon.x = 30; speakerIcon.y = 30; listenButton = new createjs.Container(); listenButton.addChild(speakerBg, speakerIcon); listenButton.x = canvasWidth / 2 - 30; listenButton.y = canvasHeight - 80; listenButton.cursor = "pointer"; listenButton.shadow = new createjs.Shadow("rgba(0,0,0,0.2)", 3,3,10); listenButton.on("mousedown", function(){ this.scaleX = this.scaleY = 0.9; }); listenButton.on("pressup", function(){ this.scaleX = this.scaleY = 1; playWordSound(); }); createjs.Tween.get(listenButton, {loop: true}) .to({scaleX:1.08, scaleY:1.08}, 900, createjs.Ease.quadInOut) .to({scaleX:1, scaleY:1}, 900, createjs.Ease.quadInOut); stage.addChild(timerContainerShape, timerBarShape, homeButton, scoreText, livesText, questionText, listenButton); } function playWordSound() { if (correctWord) { let audio = new Audio("sounds/" + correctWord.name + ".mp3"); audio.play().catch(err => console.error("مشکل در پخش صدا:", err)); } } function nextRound() { clickable = true; gameObjects.forEach(obj => stage.removeChild(obj)); gameObjects = []; let numChoices = currentRound <= 3 ? 2 : Math.min(4, 2 + Math.floor(currentRound / 5)); numChoices = Math.min(numChoices, currentWords.length); let choices = getNextWords(numChoices); correctWord = choices[0]; questionText.text = `کدام تصویر لغت "${correctWord.nameFa}" است؟`; startTimer(); choices.sort(() => 0.5 - Math.random()); choices.forEach((word, index) => createWordCard(word, index, choices.length)); positionWords(); setTimeout(playWordSound, 500); } function createWordCard(word, index, total) { const size = isMobile ? 110 : 150; const container = new createjs.Container(); const card = new createjs.Shape(); card.graphics.beginFill("#fff").drawRoundRect(0, 0, size, size, 20); card.shadow = new createjs.Shadow("rgba(0,0,0,0.2)", 3, 3, 10); const emoji = new createjs.Text(word.emoji, "50px Arial", "#000"); emoji.textAlign = "center"; emoji.textBaseline = "middle"; emoji.x = size / 2; emoji.y = size / 2 - 20; const fontSizeEn = Math.floor(size / 7); const fontSizeFa = Math.floor(size / 9); const labelEn = new createjs.Text(word.name, `bold ${fontSizeEn}px Arial`, "#333"); labelEn.textAlign = "center"; labelEn.x = size / 2; labelEn.y = size - fontSizeEn * 2.2; const labelFa = new createjs.Text(word.nameFa, `${fontSizeFa}px Vazir`, "#666"); labelFa.alpha = 0.6; labelFa.textAlign = "center"; labelFa.x = size / 2; labelFa.y = size - fontSizeFa * 1.2; container.addChild(card, emoji, labelEn, labelFa); container.regX = size / 2; container.regY = size / 2; container.word = word; container.cursor = "pointer"; container.on("click", () => handleAnswer(word, container)); stage.addChild(container); gameObjects.push(container); } function positionWords() { const total = gameObjects.length; if (total === 0) return; const size = isMobile ? 110 : 150; const padding = 20; let cols = 2; if (!isMobile) { cols = Math.min(4, total); } const rows = Math.ceil(total / cols); const totalWidth = cols * size + (cols - 1) * padding; const topMargin = 120; const bottomMargin = 100; const availableHeight = canvasHeight - topMargin - bottomMargin; const totalHeight = rows * size + (rows - 1) * padding; const startX = (canvasWidth / 2) - (totalWidth / 2) + (size / 2); const startY = topMargin + (availableHeight / 2) - (totalHeight / 2) + (size/2); gameObjects.forEach((obj, index) => { const row = Math.floor(index / cols); const col = index % cols; obj.x = startX + col * (size + padding); obj.y = startY + row * (size + padding); }); } function startTimer() { clearInterval(timerInterval); timeRemaining = timeLimit; timerInterval = setInterval(() => { timeRemaining -= 100; const percentage = Math.max(0, timeRemaining / timeLimit); let color = "#4CAF50"; if (percentage < 0.5) color = "#FF9800"; if (percentage < 0.25) color = "#f44336"; timerBarShape.graphics.clear().beginFill(color).drawRoundRect(0,0,(canvasWidth - 40) * percentage,10,5); if (timeRemaining <= 0) { clearInterval(timerInterval); handleTimeout(); } }, 100); } function handleTimeout() { if (!clickable) return; clickable = false; lives--; livesText.text = lives > 0 ? '❤️'.repeat(lives) : ''; createjs.Sound.play("incorrect_sound"); updateLeitner(correctWord.id, false); const timeoutText = new createjs.Text("⏰ زمان تمام شد!", "bold 32px Arial", "#f44336"); timeoutText.textAlign = "center"; timeoutText.x = canvasWidth / 2; timeoutText.y = canvasHeight / 2; timeoutText.alpha = 0; stage.addChild(timeoutText); createjs.Tween.get(timeoutText) .to({alpha:1}, 300) .wait(1000) .to({alpha:0}, 300) .call(() => { stage.removeChild(timeoutText); if (lives <= 0) gameOver(); else nextRound(); }); } function handleAnswer(selectedWord, selectedCard) { if (!clickable) return; clickable = false; clearInterval(timerInterval); const isCorrect = (selectedWord.id === correctWord.id); updateLeitner(selectedWord.id, isCorrect); if (isCorrect) { createjs.Sound.play("correct_sound"); streakCount++; const bonusPoints = Math.min(streakCount * 5, 25); const timeBonus = Math.floor((timeRemaining / timeLimit) * 10); const points = 10 + bonusPoints + timeBonus; score += points; createjs.Tween.get(selectedCard) .to({scaleX:1.2, scaleY:1.2, rotation:15}, 200, createjs.Ease.quadOut) .to({scaleX:1, scaleY:1, rotation:0}, 200, createjs.Ease.quadIn) .wait(500) .call(() => { currentRound++; timeLimit = Math.max(5000, 10000 - (currentRound * 150)); nextRound(); }); const bonusText = new createjs.Text(`+${points}`, "bold 28px Arial", "#4CAF50"); bonusText.textAlign = "center"; bonusText.x = selectedCard.x; bonusText.y = selectedCard.y - 50; bonusText.alpha = 0; stage.addChild(bonusText); createjs.Tween.get(bonusText) .to({alpha:1, y:bonusText.y - 30}, 500, createjs.Ease.backOut) .wait(500) .to({alpha:0}, 300) .call(() => stage.removeChild(bonusText)); } else { createjs.Sound.play("incorrect_sound"); streakCount = 0; lives--; createjs.Tween.get(selectedCard) .to({x: selectedCard.x - 10}, 50) .to({x: selectedCard.x + 20}, 100) .to({x: selectedCard.x - 20}, 100) .to({x: selectedCard.x + 10}, 50) .to({x: selectedCard.x, alpha: 0.3}, 200) .wait(800) .call(() => { if (lives <= 0) gameOver(); else nextRound(); }); const wrongText = new createjs.Text("❌", "40px Arial", "#f44336"); wrongText.textAlign = "center"; wrongText.x = selectedCard.x; wrongText.y = selectedCard.y; wrongText.alpha = 0; stage.addChild(wrongText); createjs.Tween.get(wrongText) .to({alpha:1, scaleX:1.5, scaleY:1.5}, 300) .to({alpha:0}, 500) .call(() => stage.removeChild(wrongText)); } scoreText.text = `امتیاز: ${score}`; livesText.text = lives > 0 ? '❤️'.repeat(lives) : ''; } function gameOver() { console.log("Game Over called!"); // Debug createjs.Sound.play("gameover_sound"); clearInterval(timerInterval); saveBestScore(); // توقف کامل تمام انیمیشن‌ها createjs.Tween.removeAllTweens(); // پاک کردن صحنه stage.removeAllChildren(); // تنظیم مجدد ابعاد checkDevice(); // محاسبه موقعیت‌ها const centerX = canvasWidth / 2; const centerY = canvasHeight / 2; console.log(`Canvas size: ${canvasWidth}x${canvasHeight}`); // Debug // متن پایان بازی const gameOverText = new createjs.Text("پایان بازی", "bold 36px Arial", "#e74c3c"); gameOverText.textAlign = "center"; gameOverText.x = centerX; gameOverText.y = centerY - 120; gameOverText.alpha = 0; // امتیاز نهایی const finalScoreText = new createjs.Text(`امتیاز نهایی: ${score}`, "28px Arial", "#333"); finalScoreText.textAlign = "center"; finalScoreText.x = centerX; finalScoreText.y = centerY - 70; finalScoreText.alpha = 0; // بهترین امتیاز const bestScoreText = new createjs.Text(`بهترین امتیاز: ${bestScore}`, "24px Arial", "#666"); bestScoreText.textAlign = "center"; bestScoreText.x = centerX; bestScoreText.y = centerY - 30; bestScoreText.alpha = 0; // مرحله const roundText = new createjs.Text(`مرحله: ${currentRound}`, "20px Arial", "#666"); roundText.textAlign = "center"; roundText.x = centerX; roundText.y = centerY + 10; roundText.alpha = 0; // دکمه‌ها const restartButton = createButton("🔄 بازی مجدد", "#3498db", startGame); restartButton.x = centerX; restartButton.y = centerY + 70; restartButton.alpha = 0; const menuButton = createButton("🏠 منوی اصلی", "#9b59b6", returnToMenu); menuButton.x = centerX; menuButton.y = centerY + 130; menuButton.alpha = 0; // اضافه کردن به صحنه stage.addChild(gameOverText, finalScoreText, bestScoreText, roundText, restartButton, menuButton); console.log("Elements added to stage"); // Debug // انیمیشن ورود createjs.Tween.get(gameOverText).to({alpha:1}, 500); createjs.Tween.get(finalScoreText).wait(200).to({alpha:1}, 500); createjs.Tween.get(bestScoreText).wait(400).to({alpha:1}, 500); createjs.Tween.get(roundText).wait(600).to({alpha:1}, 500); createjs.Tween.get(restartButton).wait(800).to({alpha:1}, 500); createjs.Tween.get(menuButton).wait(1000).to({alpha:1}, 500); } // ===== توابع کمکی ===== function createButton(text, color, onClick) { const button = new createjs.Container(); const bg = new createjs.Shape(); const width = isMobile ? 180 : 220; const height = 50; bg.graphics.beginLinearGradientFill([color, shadeColor(color, -20)], [0,1], 0,0,0,height) .drawRoundRect(0,0,width,height,25); const label = new createjs.Text(text, "bold 18px Arial", "#fff"); label.textAlign = "center"; label.textBaseline = "middle"; label.x = width / 2; label.y = height / 2; button.addChild(bg, label); button.regX = width / 2; button.regY = height / 2; button.cursor = "pointer"; button.shadow = new createjs.Shadow("rgba(0,0,0,0.3)",3,3,10); button.on("mousedown", function(){ this.scaleX = this.scaleY = 0.95; }); button.on("pressup", function(){ this.scaleX = this.scaleY = 1; onClick(); }); return button; } function createHomeButton(onClick) { const button = new createjs.Container(); const bg = new createjs.Shape(); bg.graphics.beginFill("#bdc3c7").drawCircle(32, 32, 32); const icon = new createjs.Text("🏠", "30px Arial", "#fff"); icon.textAlign = "center"; icon.textBaseline = "middle"; icon.x =32; icon.y = 32; button.addChild(bg, icon); button.cursor = "pointer"; button.shadow = new createjs.Shadow("rgba(0,0,0,0.2)", 2, 2, 5); button.on("mousedown", function(){ this.scaleX = this.scaleY = 0.9; }); button.on("pressup", function(){ this.scaleX = this.scaleY = 1; onClick(); }); return button; } function shadeColor(color, percent) { const num = parseInt(color.replace("#",""),16); const amt = Math.round(2.55 * percent); let R = (num >> 16) + amt; let G = (num >> 8 & 0x00FF) + amt; let B = (num & 0x0000FF) + amt; R = Math.max(0, Math.min(255, R)); G = Math.max(0, Math.min(255, G)); B = Math.max(0, Math.min(255, B)); return "#" + ( (1<<24) + (R<<16) + (G<<8) + B ).toString(16).slice(1); } class="logo"> saeidBahrami.ir