401 lines
13 KiB
JavaScript
401 lines
13 KiB
JavaScript
// 英语学习网页主脚本
|
||
|
||
// 游戏状态
|
||
let gameState = {
|
||
currentSceneIndex: 0,
|
||
currentDialogIndex: 0,
|
||
score: 0,
|
||
totalQuestions: 0,
|
||
correctAnswers: 0
|
||
};
|
||
|
||
// DOM元素
|
||
let elements;
|
||
|
||
// 在DOM加载完成后获取DOM元素
|
||
function getDomElements() {
|
||
elements = {
|
||
scoreElement: document.getElementById('score'),
|
||
progressElement: document.getElementById('progress'),
|
||
sceneTitle: document.getElementById('scene-title'),
|
||
sceneDescription: document.getElementById('scene-description'),
|
||
sceneImage: document.getElementById('scene-image'),
|
||
conversation: document.getElementById('conversation'),
|
||
questionArea: document.getElementById('question-area'),
|
||
questionText: document.getElementById('question-text'),
|
||
options: document.querySelectorAll('.option'),
|
||
feedback: document.getElementById('feedback'),
|
||
feedbackText: document.getElementById('feedback-text'),
|
||
startButton: document.getElementById('start-button'),
|
||
resetButton: document.getElementById('reset-button'),
|
||
nextSceneButton: document.getElementById('next-scene-button')
|
||
};
|
||
}
|
||
|
||
// 初始化游戏
|
||
function initGame() {
|
||
resetGameState();
|
||
loadScene(gameState.currentSceneIndex);
|
||
updateProgress();
|
||
}
|
||
|
||
// 重置游戏状态
|
||
function resetGameState() {
|
||
gameState = {
|
||
currentSceneIndex: 0,
|
||
currentDialogIndex: 0,
|
||
score: 0,
|
||
totalQuestions: 0,
|
||
correctAnswers: 0
|
||
};
|
||
}
|
||
|
||
// 加载场景
|
||
function loadScene(sceneIndex) {
|
||
if (sceneIndex >= englishData.scenes.length) {
|
||
showGameComplete();
|
||
return;
|
||
}
|
||
|
||
const scene = englishData.scenes[sceneIndex];
|
||
|
||
// 更新场景信息
|
||
elements.sceneTitle.textContent = scene.title;
|
||
elements.sceneDescription.textContent = scene.description;
|
||
|
||
// 加载场景图片
|
||
// 简化的图片加载逻辑:优先使用场景ID作为图片名称
|
||
let imagePath = scene.image;
|
||
|
||
// 确保图片元素存在
|
||
if (!elements.sceneImage) {
|
||
console.error('场景图片元素不存在');
|
||
return;
|
||
}
|
||
|
||
// 重置图片状态
|
||
elements.sceneImage.src = '';
|
||
elements.sceneImage.alt = scene.title + '场景插图';
|
||
elements.sceneImage.style.display = 'none';
|
||
|
||
// 如果没有指定图片路径,则使用场景ID作为图片名称自动查找
|
||
if (!imagePath) {
|
||
// 简单的命名规则:images/[场景ID].[扩展名] 或 images/[场景标题拼音].[扩展名]
|
||
const possibleExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.svg'];
|
||
const baseNameId = scene.id.toString();
|
||
|
||
// 将场景标题转换为拼音风格的文件名(去掉空格和特殊字符,转为小写)
|
||
const baseNameTitle = scene.title
|
||
.replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '') // 保留中英文数字
|
||
.toLowerCase();
|
||
|
||
// 所有可能的图片路径(优先ID命名,然后标题拼音命名)
|
||
const allPossiblePaths = [
|
||
// 优先使用ID命名
|
||
...possibleExtensions.map(ext => `images/${baseNameId}${ext}`),
|
||
// 也支持标题拼音命名
|
||
...possibleExtensions.map(ext => `images/${baseNameTitle}${ext}`)
|
||
];
|
||
|
||
console.log('尝试查找图片路径:', allPossiblePaths);
|
||
|
||
// 递归检查图片是否存在
|
||
function checkImage(index) {
|
||
if (index >= allPossiblePaths.length) {
|
||
console.log('未找到任何图片');
|
||
return;
|
||
}
|
||
|
||
const potentialPath = allPossiblePaths[index];
|
||
console.log('尝试加载图片:', potentialPath);
|
||
|
||
const img = new Image();
|
||
|
||
img.onload = function() {
|
||
console.log('图片加载成功:', potentialPath);
|
||
// 图片存在,设置路径并显示
|
||
elements.sceneImage.src = potentialPath;
|
||
elements.sceneImage.style.display = 'block';
|
||
};
|
||
|
||
img.onerror = function() {
|
||
console.log('图片加载失败:', potentialPath);
|
||
// 图片不存在,检查下一个
|
||
checkImage(index + 1);
|
||
};
|
||
|
||
img.src = potentialPath;
|
||
}
|
||
|
||
// 开始检查
|
||
checkImage(0);
|
||
} else {
|
||
console.log('使用指定的图片路径:', imagePath);
|
||
// 使用指定的图片路径
|
||
elements.sceneImage.src = imagePath;
|
||
elements.sceneImage.onload = function() {
|
||
elements.sceneImage.style.display = 'block';
|
||
};
|
||
elements.sceneImage.onerror = function() {
|
||
console.error('指定的图片路径加载失败:', imagePath);
|
||
elements.sceneImage.style.display = 'none';
|
||
};
|
||
}
|
||
|
||
// 清空对话区域
|
||
elements.conversation.innerHTML = '';
|
||
|
||
// 重置对话索引
|
||
gameState.currentDialogIndex = 0;
|
||
|
||
// 显示第一个对话
|
||
showNextDialog();
|
||
|
||
// 更新进度
|
||
updateProgress();
|
||
|
||
// 隐藏下一场景按钮
|
||
elements.nextSceneButton.disabled = true;
|
||
}
|
||
|
||
// 显示下一个对话
|
||
function showNextDialog() {
|
||
const scene = englishData.scenes[gameState.currentSceneIndex];
|
||
const conversation = scene.conversation;
|
||
|
||
if (gameState.currentDialogIndex >= conversation.length) {
|
||
// 当前场景对话结束
|
||
if (gameState.currentSceneIndex < englishData.scenes.length - 1) {
|
||
elements.nextSceneButton.disabled = false;
|
||
}
|
||
return;
|
||
}
|
||
|
||
const dialog = conversation[gameState.currentDialogIndex];
|
||
|
||
if (dialog.type === 'user_question') {
|
||
// 显示选择题
|
||
showQuestion(dialog);
|
||
} else {
|
||
// 显示普通对话
|
||
addMessage(dialog);
|
||
|
||
// 自动显示下一个对话
|
||
gameState.currentDialogIndex++;
|
||
setTimeout(showNextDialog, 1000);
|
||
}
|
||
}
|
||
|
||
// 添加对话消息
|
||
function addMessage(dialog) {
|
||
const messageDiv = document.createElement('div');
|
||
messageDiv.className = `message ${dialog.type}`;
|
||
|
||
const speakerSpan = document.createElement('span');
|
||
speakerSpan.className = 'speaker';
|
||
speakerSpan.textContent = dialog.speaker + ':';
|
||
|
||
const textSpan = document.createElement('span');
|
||
textSpan.className = 'text';
|
||
textSpan.textContent = dialog.text;
|
||
|
||
messageDiv.appendChild(speakerSpan);
|
||
messageDiv.appendChild(textSpan);
|
||
|
||
elements.conversation.appendChild(messageDiv);
|
||
|
||
// 滚动到底部
|
||
elements.conversation.scrollTop = elements.conversation.scrollHeight;
|
||
}
|
||
|
||
// 显示选择题
|
||
function showQuestion(dialog) {
|
||
elements.questionText.textContent = dialog.question;
|
||
|
||
// 复制选项数组并打乱顺序
|
||
const originalOptions = [...dialog.options];
|
||
const shuffledOptions = [...originalOptions];
|
||
|
||
// 使用Fisher-Yates算法打乱选项
|
||
for (let i = shuffledOptions.length - 1; i > 0; i--) {
|
||
const j = Math.floor(Math.random() * (i + 1));
|
||
[shuffledOptions[i], shuffledOptions[j]] = [shuffledOptions[j], shuffledOptions[i]];
|
||
}
|
||
|
||
// 计算正确答案在打乱后的新位置
|
||
const correctAnswerText = originalOptions[dialog.correctAnswer];
|
||
const newCorrectIndex = shuffledOptions.indexOf(correctAnswerText);
|
||
|
||
// 保存原始选项和新的正确答案索引到对话对象中,供后续检查使用
|
||
dialog.originalOptions = originalOptions;
|
||
dialog.shuffledOptions = shuffledOptions;
|
||
dialog.shuffledCorrectAnswer = newCorrectIndex;
|
||
|
||
// 设置打乱后的选项
|
||
elements.options.forEach((option, index) => {
|
||
option.textContent = shuffledOptions[index];
|
||
option.disabled = false;
|
||
option.classList.remove('selected', 'correct', 'incorrect');
|
||
});
|
||
|
||
// 显示选择题区域
|
||
elements.questionArea.style.display = 'block';
|
||
elements.feedback.style.display = 'none';
|
||
}
|
||
|
||
// 处理选项选择
|
||
function handleOptionSelect(selectedOptionIndex) {
|
||
const scene = englishData.scenes[gameState.currentSceneIndex];
|
||
const dialog = scene.conversation[gameState.currentDialogIndex];
|
||
// 使用打乱后的正确答案索引
|
||
const correctAnswerIndex = dialog.shuffledCorrectAnswer !== undefined ? dialog.shuffledCorrectAnswer : dialog.correctAnswer;
|
||
|
||
// 禁用所有选项
|
||
elements.options.forEach(option => {
|
||
option.disabled = true;
|
||
});
|
||
|
||
// 标记选择的选项
|
||
const selectedOption = elements.options[selectedOptionIndex];
|
||
selectedOption.classList.add('selected');
|
||
|
||
// 检查答案
|
||
gameState.totalQuestions++;
|
||
let isCorrect = false;
|
||
|
||
if (selectedOptionIndex === correctAnswerIndex) {
|
||
// 正确答案
|
||
gameState.score += 10;
|
||
gameState.correctAnswers++;
|
||
selectedOption.classList.add('correct');
|
||
showFeedback(true, dialog);
|
||
isCorrect = true;
|
||
} else {
|
||
// 错误答案
|
||
selectedOption.classList.add('incorrect');
|
||
elements.options[correctAnswerIndex].classList.add('correct');
|
||
showFeedback(false, dialog);
|
||
}
|
||
|
||
// 更新得分和进度
|
||
updateProgress();
|
||
|
||
// 添加用户选择的对话
|
||
addUserChoiceToConversation(dialog, selectedOptionIndex, isCorrect);
|
||
|
||
// 延迟后显示下一个对话
|
||
setTimeout(() => {
|
||
gameState.currentDialogIndex++;
|
||
elements.questionArea.style.display = 'none';
|
||
elements.feedback.style.display = 'none';
|
||
showNextDialog();
|
||
}, 3000);
|
||
}
|
||
|
||
// 添加用户选择到对话
|
||
function addUserChoiceToConversation(dialog, selectedIndex, isCorrect) {
|
||
const userMessage = {
|
||
speaker: dialog.speaker,
|
||
text: dialog.options[selectedIndex],
|
||
type: 'user'
|
||
};
|
||
|
||
addMessage(userMessage);
|
||
}
|
||
|
||
// 显示反馈
|
||
function showFeedback(isCorrect, dialog) {
|
||
const feedbackTextElement = elements.feedbackText;
|
||
|
||
if (isCorrect) {
|
||
// 正确答案
|
||
feedbackTextElement.textContent = '正确! 很好的回答。';
|
||
feedbackTextElement.className = 'correct';
|
||
elements.feedback.style.borderColor = '#4CAF50';
|
||
elements.feedback.style.backgroundColor = '#e8f5e8';
|
||
} else {
|
||
// 错误答案
|
||
// 根据是否有打乱后的选项来选择使用哪个数组
|
||
const correctAnswer = dialog.shuffledOptions && dialog.shuffledCorrectAnswer !== undefined
|
||
? dialog.shuffledOptions[dialog.shuffledCorrectAnswer]
|
||
: dialog.options[dialog.correctAnswer];
|
||
|
||
feedbackTextElement.textContent = `不正确。正确答案是: ${correctAnswer}`;
|
||
feedbackTextElement.className = 'incorrect';
|
||
elements.feedback.style.borderColor = '#f44336';
|
||
elements.feedback.style.backgroundColor = '#ffebee';
|
||
}
|
||
|
||
elements.feedback.style.display = 'block';
|
||
}
|
||
|
||
// 更新进度
|
||
function updateProgress() {
|
||
elements.scoreElement.textContent = `得分: ${gameState.score}`;
|
||
elements.progressElement.textContent = `进度: ${gameState.currentSceneIndex + 1}/${englishData.scenes.length}`;
|
||
}
|
||
|
||
// 显示游戏完成信息
|
||
function showGameComplete() {
|
||
elements.conversation.innerHTML = '';
|
||
elements.questionArea.style.display = 'none';
|
||
|
||
const completeMessage = document.createElement('div');
|
||
completeMessage.className = 'message other';
|
||
completeMessage.innerHTML = `
|
||
<span class="speaker">学习完成:</span>
|
||
<span class="text">
|
||
恭喜您完成了所有场景的学习!<br>
|
||
总得分: ${gameState.score}<br>
|
||
正确题目: ${gameState.correctAnswers}/${gameState.totalQuestions}
|
||
</span>
|
||
`;
|
||
|
||
elements.conversation.appendChild(completeMessage);
|
||
elements.nextSceneButton.disabled = true;
|
||
}
|
||
|
||
// 设置事件监听器
|
||
function setupEventListeners() {
|
||
// 选项按钮点击事件
|
||
elements.options.forEach((option, index) => {
|
||
option.addEventListener('click', () => {
|
||
handleOptionSelect(index);
|
||
});
|
||
});
|
||
|
||
// 开始按钮点击事件
|
||
elements.startButton.addEventListener('click', () => {
|
||
initGame();
|
||
elements.startButton.style.display = 'none';
|
||
});
|
||
|
||
// 重置按钮点击事件
|
||
elements.resetButton.addEventListener('click', () => {
|
||
initGame();
|
||
elements.startButton.style.display = 'none';
|
||
});
|
||
|
||
// 下一场景按钮点击事件
|
||
elements.nextSceneButton.addEventListener('click', () => {
|
||
gameState.currentSceneIndex++;
|
||
loadScene(gameState.currentSceneIndex);
|
||
});
|
||
}
|
||
|
||
// 页面加载完成后初始化游戏
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
// 获取DOM元素
|
||
getDomElements();
|
||
|
||
// 设置事件监听器(只调用一次)
|
||
setupEventListeners();
|
||
|
||
// 确保englishData已加载
|
||
if (typeof englishData !== 'undefined') {
|
||
initGame();
|
||
elements.startButton.style.display = 'block';
|
||
} else {
|
||
console.error('英语学习数据未加载');
|
||
}
|
||
}); |