// 游戏状态 const gameState = { governor: '', // 玩家名称 reignTime: 0, // 当前统治时间 year: 41000, // 当前年份 stats: { loyalty: 50, // 帝国忠诚度 chaos: 50, // 混沌侵染 population: 50, // 民众控制 military: 50, // 军事力量 resources: 50 // 资源储备 }, statusEffects: [], // 状态效果 currentCardIndex: 0, // 当前卡片索引 isDealing: false, // 是否正在发牌 isDragging: false, // 是否正在拖动 gameOver: false, // 游戏是否结束 gameOverReason: '', // 游戏结束原因 characters: [], // 角色数据 availableCharacters: [], // 可用角色池 currentEffects: null, // 当前卡片效果 currentEvent: null, // 当前事件 currentCharacter: null, // 当前角色 // 新增: 事件和状态相关属性 activeEvent: null, // 当前活跃事件 eventNode: null, // 当前事件节点 completedEvents: [], // 已完成事件 activeStatuses: [], // 当前活跃的状态效果 // 开发者模式属性 devMode: false, // 开发者模式标志 nextEventId: null, // 下一个要触发的事件ID }; // DOM 元素 const startScreen = document.querySelector('.start-screen'); const startButton = document.querySelector('.start-button'); const gameContainer = document.querySelector('.game-container'); const cardContainer = document.getElementById('card-container'); const speechBubble = document.getElementById('speech-bubble'); const gameOverDialog = document.getElementById('game-over-dialog'); const gameOverReason = document.getElementById('game-over-reason'); const finalYears = document.getElementById('final-years'); const restartButton = document.getElementById('restart-button'); const reignYearsElement = document.getElementById('reign-years'); const currentYearElement = document.getElementById('current-year'); const playerNameElement = document.getElementById('player-name'); const statusBars = { loyalty: document.getElementById('loyalty-bar'), chaos: document.getElementById('chaos-bar'), population: document.getElementById('population-bar'), military: document.getElementById('military-bar'), resources: document.getElementById('resources-bar') }; const statusIcons = { loyalty: document.getElementById('loyalty-icon'), chaos: document.getElementById('chaos-icon'), population: document.getElementById('population-icon'), military: document.getElementById('military-icon'), resources: document.getElementById('resources-icon') }; // 新增: 状态和开发者工具相关的DOM元素 const activeStatusesContainer = document.getElementById('active-statuses'); const statusDetailsDialog = document.getElementById('status-details-dialog'); const closeStatusDetailsButton = document.getElementById('close-status-details'); const devToolsButton = document.getElementById('dev-tools-button'); const devToolsDialog = document.getElementById('dev-tools-dialog'); const closeDevToolsButton = document.getElementById('close-dev-tools'); const triggerEventButton = document.getElementById('trigger-event-btn'); const eventSelector = document.getElementById('event-selector'); const statusSelector = document.getElementById('status-selector'); const addStatusButton = document.getElementById('add-status-btn'); const removeStatusButton = document.getElementById('remove-status-btn'); const statSelector = document.getElementById('stat-selector'); const statValueInput = document.getElementById('stat-value'); const setStatButton = document.getElementById('set-stat-btn'); // 拖动相关变量 let startX = 0; let currentX = 0; let activeCard = null; // 加载游戏数据 function loadGameData() { if (gameData && gameData.cards) { gameState.characters = gameData.cards.characters; // 初始化可用角色池 - 仅包含常驻角色 gameState.availableCharacters = gameState.characters .filter(char => char.type === 'resident') .map(char => char.id); } // 获取上次保存的年份 if (gameData && gameData.lastYear) { gameState.year = gameData.lastYear; } // 从服务器加载保存的游戏状态 fetch('/api/load_game') .then(response => response.json()) .then(data => { if (data.game_data) { // 恢复保存的游戏状态 Object.assign(gameState, data.game_data); updateUI(); } else if (data.lastYear) { // 如果没有游戏状态但有最后年份,设置年份 gameState.year = data.lastYear; updateUI(); } }) .catch(error => console.error('加载游戏状态失败:', error)); // 设置初始总督名称 gameState.governor = document.querySelector('#player-name').textContent || '总督'; // 检查是否启用开发者模式 checkDevMode(); // 初始化开发者工具中的事件和状态选择器 initDevTools(); } // 开始游戏事件 startButton.addEventListener('click', startGame); // 开始游戏函数 function startGame() { startScreen.style.opacity = '0'; setTimeout(() => { startScreen.style.display = 'none'; gameContainer.style.opacity = '1'; initGame(); }, 1000); } // 初始化游戏 function initGame() { // 保留当前年份 const currentYear = gameState.year; // 重置游戏状态为默认值 gameState.reignTime = 0; gameState.year = currentYear; // 使用之前的年份而非固定值 gameState.stats = { loyalty: 50, chaos: 50, population: 50, military: 50, resources: 50 }; gameState.statusEffects = []; gameState.currentCardIndex = 0; gameState.isDealing = false; gameState.isDragging = false; gameState.gameOver = false; gameState.gameOverReason = ''; gameState.currentEffects = null; gameState.currentEvent = null; gameState.currentCharacter = null; // 重置事件和状态相关属性 gameState.activeEvent = null; gameState.eventNode = null; gameState.completedEvents = []; gameState.activeStatuses = []; // 重新洗牌可用角色 shuffleAvailableCharacters(); // 更新UI updateUI(); // 清空卡容器 cardContainer.innerHTML = ''; // 隐藏对话气泡 speechBubble.classList.remove('visible'); speechBubble.textContent = ''; // 移除所有效果指示器 removeAllEffectIndicators(); // 清空状态图标区域 activeStatusesContainer.innerHTML = ''; // 开始发牌动画 startDealingCards(); } // 检查是否启用开发者模式 function checkDevMode() { if (gameState.governor === '1') { gameState.devMode = true; devToolsButton.style.display = 'flex'; } else { gameState.devMode = false; devToolsButton.style.display = 'none'; } } // 初始化开发者工具 function initDevTools() { // 检查数据结构 if (!gameData) return; // 清空现有选项 eventSelector.innerHTML = ''; statusSelector.innerHTML = ''; // 添加事件选项 - 考虑两种可能的数据结构 const events = gameData.events || (gameData.cards && gameData.cards.events); if (events && events.length > 0) { events.forEach(event => { const option = document.createElement('option'); option.value = event.id; option.textContent = event.name; eventSelector.appendChild(option); }); } else { console.warn('未找到事件数据'); } // 添加状态选项 - 考虑两种可能的数据结构 const statuses = gameData.statuses || (gameData.cards && gameData.cards.statuses); if (statuses && statuses.length > 0) { statuses.forEach(status => { const option = document.createElement('option'); option.value = status.id; option.textContent = status.name; statusSelector.appendChild(option); }); } else { console.warn('未找到状态数据'); } // 记录一下数据结构以进行调试 console.log('GameData structure:', gameData); } // 添加状态影响指示器 function addEffectIndicators() { // 先移除所有现有指示器 removeAllEffectIndicators(); if (!gameState.currentEffects) return; // 为每个状态添加指示器 for (const stat in gameState.currentEffects) { const value = gameState.currentEffects[stat]; if (value === 0) continue; // 跳过无影响的属性 const iconElement = statusIcons[stat].parentElement; // 创建指示器容器 const indicator = document.createElement('div'); indicator.className = 'status-indicator'; indicator.classList.add(value > 0 ? 'positive-effect' : 'negative-effect'); // 根据影响值大小决定显示大圆点还是小圆点 const absValue = Math.abs(value); if (absValue >= 15) { // 大影响,显示大圆点 const dot = document.createElement('div'); dot.className = 'indicator-dot large'; indicator.appendChild(dot); } else if (absValue >= 10) { // 中等影响,显示一个小圆点 const dot = document.createElement('div'); dot.className = 'indicator-dot small'; indicator.appendChild(dot); } else { // 小影响,不显示圆点 // 或者也可以显示一个更小的圆点 const dot = document.createElement('div'); dot.className = 'indicator-dot small'; dot.style.width = '5px'; dot.style.height = '5px'; indicator.appendChild(dot); } // 添加到状态图标元素 iconElement.appendChild(indicator); } } // 移除所有效果指示器 function removeAllEffectIndicators() { document.querySelectorAll('.status-indicator').forEach(indicator => { indicator.remove(); }); } // 重新构建可用角色池,考虑权重 function shuffleAvailableCharacters() { // 清空可用角色池 gameState.availableCharacters = []; // 筛选常驻角色 const residentCharacters = gameState.characters.filter(char => char.type === 'resident'); // 根据权重添加角色ID到可用池中 residentCharacters.forEach(character => { const weight = character.weight || 100; // 默认权重为100 // 根据权重添加多次角色ID,增加被选中概率 for (let i = 0; i < weight; i++) { gameState.availableCharacters.push(character.id); } }); // 打乱角色顺序 gameState.availableCharacters.sort(() => Math.random() - 0.5); } // 显示对话气泡 function showSpeechBubble(text) { speechBubble.textContent = text; speechBubble.classList.add('visible'); } // 隐藏对话气泡 function hideSpeechBubble() { speechBubble.classList.remove('visible'); } // 开始发牌动画 function startDealingCards() { gameState.isDealing = true; let cardsDealt = 0; const totalCards = 5; // 发5张牌 for (let i = 0; i < totalCards; i++) { setTimeout(() => { const card = document.createElement('div'); card.className = 'card card-back'; // 设置初始位置 - 左上角45度 card.style.transform = 'translate(-150px, -150px) rotate(-45deg) scale(0.8)'; card.style.opacity = '0'; card.style.transition = 'none'; // 设置层级,越后面的卡片层级越低 card.style.zIndex = `${5 - i}`; cardContainer.appendChild(card); // 强制重绘以确保过渡效果正常 void card.offsetWidth; // 添加过渡属性 card.style.transition = 'transform 0.4s ease, opacity 0.4s ease'; // 设置终点位置 - 所有牌都落在中心位置,但有微小偏移以显示后面还有牌 setTimeout(() => { // 计算每张牌的偏移,让它们有细微重叠 const offsetX = i * 2; // 每张牌向右偏移2px const offsetY = i * 2; // 每张牌向下偏移2px const randomAngle = (Math.random() - 0.5) * 2; // 添加微小随机角度 card.style.transform = `translate(${offsetX}px, ${offsetY}px) rotate(${randomAngle}deg)`; card.style.opacity = '1'; // 给静态背景牌添加一个类,方便后续识别 if (i > 0) { card.classList.add('static-background-card'); } // 计数已发的牌 cardsDealt++; // 全部发完后,等待一小段时间再翻转第一张牌 if (cardsDealt === totalCards) { setTimeout(() => { // 翻转最上面的牌 const topCard = cardContainer.querySelector('.card-back:not(.static-background-card)'); if (topCard) { // 检查是否有指定的下一个事件 if (gameState.devMode && gameState.nextEventId) { startEvent(gameState.nextEventId); gameState.nextEventId = null; // 重置 } else { // 检查是否应该触发随机事件 const eventToTrigger = checkEventTriggers(); if (eventToTrigger) { startEvent(eventToTrigger); } else { flipTopCard(topCard); } } } // 结束发牌状态 gameState.isDealing = false; }, 600); } }, 50); }, i * 200); // 每张牌间隔200ms发出 } } // 检查是否应该触发事件 function checkEventTriggers() { if (!gameData || !gameData.events) return null; // 筛选可能触发的事件 const possibleEvents = gameData.events.filter(event => { // 跳过已完成的事件 if (gameState.completedEvents.includes(event.id)) return false; // 检查触发条件 if (event.trigger) { if (event.trigger.type === 'random') { // 随机触发事件 - 按概率判断 return Math.random() < (event.trigger.probability || 0.1); } else if (event.trigger.type === 'condition') { // 条件触发事件 const stat = event.trigger.stat; const condition = event.trigger.condition; const value = event.trigger.value; if (stat && condition && value !== undefined) { const currentValue = gameState.stats[stat]; switch (condition) { case 'less_than': return currentValue < value; case 'greater_than': return currentValue > value; case 'equal': return currentValue === value; } } } else if (event.trigger.type === 'status') { // 状态触发事件 const statusId = event.trigger.statusId; const condition = event.trigger.condition || 'exists'; const hasStatus = gameState.activeStatuses.some(s => s.id === statusId); return condition === 'exists' ? hasStatus : !hasStatus; } } return false; }); // 如果有可能触发的事件,随机选择一个 if (possibleEvents.length > 0) { const randomIndex = Math.floor(Math.random() * possibleEvents.length); return possibleEvents[randomIndex].id; } return null; } // 开始一个事件 function startEvent(eventId) { if (!gameData || !gameData.events) return; // 查找事件数据 const eventData = gameData.events.find(e => e.id === eventId); if (!eventData || !eventData.nodes || eventData.nodes.length === 0) return; // 设置当前事件 gameState.activeEvent = eventData; gameState.eventNode = eventData.nodes[0]; // 从第一个节点开始 // 显示第一个节点的卡片 showEventCard(gameState.eventNode); } // 显示事件卡片 function showEventCard(node) { if (!node) return; // 查找角色数据 const character = gameState.characters.find(c => c.id === node.character); if (!character) return; // 清理现有卡片和信息 const existingFrontCard = document.getElementById('active-card'); if (existingFrontCard) { existingFrontCard.remove(); } const existingCharInfo = document.querySelector('.character-info'); if (existingCharInfo) { existingCharInfo.remove(); } // 创建角色信息区域 const characterInfo = document.createElement('div'); characterInfo.className = 'character-info'; characterInfo.innerHTML = `
${character.name}
${character.title}
`; characterInfo.style.opacity = '0'; characterInfo.style.zIndex = '20'; // 检查角色是否有图片路径 const hasImage = character.avatarPath && character.avatarPath.length > 0; const portraitContent = hasImage ? `${character.name}` : `
${character.avatar}
`; // 创建事件卡片 const eventCard = document.createElement('div'); eventCard.className = 'card'; eventCard.id = 'active-card'; eventCard.dataset.eventId = gameState.activeEvent.id; eventCard.dataset.nodeId = node.id; eventCard.style.zIndex = '15'; // 设置卡片内容 - 包括选项 eventCard.innerHTML = `
${portraitContent}
${node.options[0].text}
${node.options.length > 1 ? node.options[1].text : node.options[0].text}
`; // 添加到DOM cardContainer.appendChild(characterInfo); cardContainer.appendChild(eventCard); // 设置卡片初始样式 eventCard.style.transform = 'rotateY(-90deg)'; eventCard.style.opacity = '0'; eventCard.style.transition = 'none'; // 强制重绘 void eventCard.offsetWidth; // 寻找一张背面牌用于翻转效果 const topBackCard = cardContainer.querySelector('.card-back:not(.static-background-card)'); if (topBackCard) { // 背面牌开始翻转 topBackCard.style.transition = 'all 0.4s ease'; topBackCard.style.transform = 'rotateY(90deg)'; topBackCard.style.opacity = '0'; topBackCard.style.zIndex = '5'; } // 等待背面牌翻转到一半后,开始前面牌的翻转 setTimeout(() => { // 前面卡片开始翻转 eventCard.style.transition = 'all 0.4s ease'; eventCard.style.transform = 'rotateY(0deg)'; eventCard.style.opacity = '1'; // 设置为当前活动卡片 activeCard = eventCard; // 显示对话气泡 showSpeechBubble(node.text); // 显示角色信息 setTimeout(() => { characterInfo.style.opacity = '1'; // 添加拖动事件 setupCardDrag(eventCard, true); // 标记为事件卡片 // 移除原来的背面牌 if (topBackCard && topBackCard.parentNode) { topBackCard.remove(); } // 添加一张新的背面牌到牌堆顶部 addNewBackCard(); }, 400); }, 200); } // 继续事件处理 function continueEvent(optionIndex) { if (!gameState.activeEvent || !gameState.eventNode) { console.error("无法继续事件:事件数据缺失"); return; } // 获取选中的选项 const options = gameState.eventNode.options; if (!options || !options[optionIndex]) { console.error("无法继续事件:选项数据缺失"); return; } const option = options[optionIndex]; // 应用选项的效果 if (option.effects) { applyEffects(option.effects); } // 添加状态 if (option.add_status) { addStatus(option.add_status); } // 移除状态 if (option.remove_status) { removeStatus(option.remove_status); } // 检查下一个节点 if (option.next_node && option.next_node !== 'end') { // 查找下一个节点 const nextNode = gameState.activeEvent.nodes.find(n => n.id === option.next_node); if (nextNode) { // 更新当前节点 gameState.eventNode = nextNode; // 显示下一个节点的卡片 showEventCard(nextNode); return; } } // 如果没有下一个节点或者是结束节点,结束事件 endEvent(); } // 结束当前事件 function endEvent() { // 将事件添加到已完成列表 if (gameState.activeEvent) { gameState.completedEvents.push(gameState.activeEvent.id); } // 重置事件状态 gameState.activeEvent = null; gameState.eventNode = null; // 继续游戏流程 - 翻转一张新卡片 setTimeout(() => { if (!gameState.gameOver) { flipTopCard(); } }, 500); } // 找到flipTopCard函数,修改角色选择部分(以下是完整函数) function flipTopCard(topBackCard) { // 如果没有可用角色,则重新洗牌 if (gameState.availableCharacters.length === 0) { shuffleAvailableCharacters(); } // 从可用角色池中随机选择一个角色ID(权重已经在池中体现) const randomIndex = Math.floor(Math.random() * gameState.availableCharacters.length); const characterId = gameState.availableCharacters[randomIndex]; // 从可用池中移除该角色ID gameState.availableCharacters.splice(randomIndex, 1); // 获取角色数据 const character = gameState.characters.find(c => c.id === characterId); if (!character || !character.events || character.events.length === 0) { // 如果角色无效或没有事件,则跳过并尝试下一个 console.warn("角色无效或没有事件:", characterId); flipTopCard(topBackCard); return; } // 从角色的事件中随机选择一个 const eventIndex = Math.floor(Math.random() * character.events.length); const event = character.events[eventIndex]; // 保存当前事件和角色及其效果 gameState.currentEvent = event; gameState.currentCharacter = character; gameState.currentEffects = { loyalty: event.optionA.effects.loyalty, chaos: event.optionA.effects.chaos, population: event.optionA.effects.population, military: event.optionA.effects.military, resources: event.optionA.effects.resources }; // 如果没有传入顶部牌,则找到当前最上面的背面牌 if (!topBackCard) { topBackCard = cardContainer.querySelector('.card-back:not(.static-background-card)'); if (!topBackCard) return; // 如果没有找到,直接返回 } // 清理可能存在的旧元素 const existingFrontCard = document.getElementById('active-card'); if (existingFrontCard) { existingFrontCard.remove(); } const existingCharInfo = document.querySelector('.character-info'); if (existingCharInfo) { existingCharInfo.remove(); } // 创建角色信息区域 const characterInfo = document.createElement('div'); characterInfo.className = 'character-info'; characterInfo.innerHTML = `
${character.name}
${character.title}
`; characterInfo.style.opacity = '0'; characterInfo.style.zIndex = '20'; // 检查角色是否有图片路径,如果没有则使用表情图标 const hasImage = character.avatarPath && character.avatarPath.length > 0; const portraitContent = hasImage ? `${character.name}` : `
${character.avatar}
`; // 创建新卡片 (前面) const frontCard = document.createElement('div'); frontCard.className = 'card'; frontCard.id = 'active-card'; frontCard.dataset.characterId = character.id; frontCard.dataset.eventId = event.id; frontCard.style.zIndex = '15'; // 设置卡片内容 frontCard.innerHTML = `
${portraitContent}
${event.optionA.text}
${event.optionB.text}
`; // 添加到DOM cardContainer.appendChild(characterInfo); cardContainer.appendChild(frontCard); // 重要:先创建元素但不添加到DOM // 设置前面卡片初始样式 - 已经旋转到-90度 frontCard.style.transform = 'rotateY(-90deg)'; frontCard.style.opacity = '0'; frontCard.style.transition = 'none'; // 强制重绘以确保初始状态被应用 void frontCard.offsetWidth; // 开始背面卡片的翻转动画 topBackCard.style.transition = 'all 0.4s ease'; topBackCard.style.transform = 'rotateY(90deg)'; topBackCard.style.opacity = '0'; // 等待背面卡片翻转到一半后,开始前面卡片的翻转 setTimeout(() => { // 设置过渡效果并开始翻转 frontCard.style.transition = 'all 0.4s ease'; frontCard.style.transform = 'rotateY(0deg)'; frontCard.style.opacity = '1'; // 设置为当前活动卡片 activeCard = frontCard; // 等动画完成后显示角色信息和添加事件 setTimeout(() => { // 显示角色信息 characterInfo.style.transition = 'opacity 0.3s ease'; characterInfo.style.opacity = '1'; // 显示对话气泡 showSpeechBubble(event.text); // 添加拖动事件 setupCardDrag(frontCard); // 移除原来的背面牌 if (topBackCard.parentNode) { topBackCard.remove(); } // 添加一张新的背面牌到牌堆顶部 addNewBackCard(); }, 400); }, 200); } // 添加一张新的背面牌到牌堆顶部 function addNewBackCard() { // 查找所有背景牌 const backgroundCards = document.querySelectorAll('.static-background-card'); if (backgroundCards.length === 0) return; // 创建一张新的背面牌 const newBackCard = document.createElement('div'); newBackCard.className = 'card card-back'; // 设置位置和样式,与其他背景牌保持一致的错开效果 // 从第一张背景牌获取位置参考 const referenceCard = backgroundCards[0]; const referenceRect = referenceCard.getBoundingClientRect(); const containerRect = cardContainer.getBoundingClientRect(); // 计算相对位置 const relativeX = referenceRect.left - containerRect.left; const relativeY = referenceRect.top - containerRect.top; // 设置新牌的位置,略微比参考牌更靠前 newBackCard.style.transform = `translate(${relativeX - 2}px, ${relativeY - 2}px)`; newBackCard.style.opacity = '1'; newBackCard.style.zIndex = '5'; // 比背景牌高,但比内容牌低 // 添加到容器 cardContainer.insertBefore(newBackCard, cardContainer.firstChild); } // 替换 setupCardDrag 函数以更清晰地设置卡片类型 function setupCardDrag(card, isEventCard = false) { // 标记卡片类型 - 确保这里明确设置了属性 if (isEventCard) { card.dataset.cardType = 'event'; } else { card.dataset.cardType = 'normal'; } // 为每张卡单独添加事件 card.addEventListener('mousedown', function(e) { onCardDragStart(e, this); }); card.addEventListener('touchstart', function(e) { onCardDragStart(e, this); }); } // 更新 onCardDragStart 不再需要传递 isEventCard 参数 function onCardDragStart(e, card) { // 如果正在发牌或已经在拖动,则不处理 if (gameState.isDealing || gameState.isDragging || gameState.gameOver) return; e.preventDefault(); gameState.isDragging = true; // 记录起始位置 startX = e.type === 'mousedown' ? e.clientX : e.touches[0].clientX; currentX = startX; // 移除过渡效果使拖动更平滑 activeCard.style.transition = 'none'; // 添加全局事件 document.addEventListener('mousemove', onCardDragMove); document.addEventListener('touchmove', onCardDragMove, { passive: false }); document.addEventListener('mouseup', onCardDragEnd); document.addEventListener('touchend', onCardDragEnd); } // 卡片拖动中 function onCardDragMove(e) { if (!gameState.isDragging) return; // 阻止触摸事件的默认行为(滚动) if (e.type === 'touchmove') { e.preventDefault(); } // 计算水平移动距离 currentX = e.type === 'mousemove' ? e.clientX : e.touches[0].clientX; const deltaX = currentX - startX; // 移动卡片,添加旋转效果 activeCard.style.transform = `translateX(${deltaX}px) rotate(${deltaX * 0.05}deg)`; // 显示选择选项和效果 updateChoiceDisplay(deltaX); } // 更新选择选项显示和效果指示器 function updateChoiceDisplay(deltaX) { if (!activeCard) return; // 获取右上角和左上角选项区域 // 注意:左滑显示右上角选项,右滑显示左上角选项 const rightOption = activeCard.querySelector('.right-option-overlay'); // 左滑-同意(蓝色) const leftOption = activeCard.querySelector('.left-option-overlay'); // 右滑-拒绝(红色) if (!rightOption || !leftOption) return; // 根据拖动距离计算透明度 // 左滑(负值)时显示右上角选项(蓝色) // 右滑(正值)时显示左上角选项(红色) const rightOptionOpacity = deltaX < 0 ? Math.min(1, Math.abs(deltaX) / 100) : 0; const leftOptionOpacity = deltaX > 0 ? Math.min(1, Math.abs(deltaX) / 100) : 0; rightOption.style.opacity = rightOptionOpacity; leftOption.style.opacity = leftOptionOpacity; // 根据拖动方向显示相应效果 // 检查是事件卡片还是普通卡片 const isEventCard = activeCard.dataset.cardType === 'event'; const isDeathCard = activeCard.classList.contains('death-card'); // 死亡卡片不显示效果 if (isDeathCard) { removeAllEffectIndicators(); return; } if (isEventCard) { // 事件卡片 - 检查当前节点选项 const node = gameState.eventNode; if (!node) return; if (deltaX < -50) { // 左滑 - 选项0的效果 if (node.options[0].effects) { showEventOptionEffects(node.options[0].effects); } else { removeAllEffectIndicators(); } } else if (deltaX > 50) { // 右滑 - 选项1的效果 const option = node.options.length > 1 ? node.options[1] : node.options[0]; if (option.effects) { showEventOptionEffects(option.effects); } else { removeAllEffectIndicators(); } } else { // 回到中间,移除效果显示 removeAllEffectIndicators(); } } else { // 普通卡片 if (deltaX < -50) { // 左滑 - 显示选项A的效果(蓝色) showOptionEffects('A'); } else if (deltaX > 50) { // 右滑 - 显示选项B的效果(红色) showOptionEffects('B'); } else { // 回到中间,移除效果显示 removeAllEffectIndicators(); } } } // 显示事件选项效果 function showEventOptionEffects(effects) { if (!effects) return; // 移除现有指示器 removeAllEffectIndicators(); // 添加新指示器 for (const stat in effects) { const value = effects[stat]; if (value === 0) continue; // 跳过无影响的属性 const iconElement = statusIcons[stat].parentElement; // 创建指示器容器 const indicator = document.createElement('div'); indicator.className = 'status-indicator'; indicator.classList.add(value > 0 ? 'positive-effect' : 'negative-effect'); indicator.style.opacity = '1'; // 确保可见 // 根据影响值大小决定显示大圆点还是小圆点 const absValue = Math.abs(value); if (absValue >= 15) { // 大影响,显示大圆点 const dot = document.createElement('div'); dot.className = 'indicator-dot large'; indicator.appendChild(dot); } else if (absValue >= 10) { // 中等影响,显示一个小圆点 const dot = document.createElement('div'); dot.className = 'indicator-dot small'; indicator.appendChild(dot); } else { // 小影响,显示一个更小的圆点 const dot = document.createElement('div'); dot.className = 'indicator-dot small'; dot.style.width = '5px'; dot.style.height = '5px'; indicator.appendChild(dot); } // 添加到状态图标元素 iconElement.appendChild(indicator); } } // 显示选项效果 function showOptionEffects(option) { if (!gameState.currentEvent) return; // 获取效果 const effects = option === 'A' ? gameState.currentEvent.optionA.effects : gameState.currentEvent.optionB.effects; // 移除现有指示器 removeAllEffectIndicators(); // 添加新指示器 for (const stat in effects) { const value = effects[stat]; if (value === 0) continue; // 跳过无影响的属性 const iconElement = statusIcons[stat].parentElement; // 创建指示器容器 const indicator = document.createElement('div'); indicator.className = 'status-indicator'; indicator.classList.add(value > 0 ? 'positive-effect' : 'negative-effect'); indicator.style.opacity = '1'; // 确保可见 // 根据影响值大小决定显示大圆点还是小圆点 const absValue = Math.abs(value); if (absValue >= 15) { // 大影响,显示大圆点 const dot = document.createElement('div'); dot.className = 'indicator-dot large'; indicator.appendChild(dot); } else if (absValue >= 10) { // 中等影响,显示一个小圆点 const dot = document.createElement('div'); dot.className = 'indicator-dot small'; indicator.appendChild(dot); } else { // 小影响,显示一个更小的圆点 const dot = document.createElement('div'); dot.className = 'indicator-dot small'; dot.style.width = '5px'; dot.style.height = '5px'; indicator.appendChild(dot); } // 添加到状态图标元素 iconElement.appendChild(indicator); } } // 替换 onCardDragEnd 函数 function onCardDragEnd(e, isEventCard) { if (!gameState.isDragging || !activeCard) return; // 计算最终移动距离 const deltaX = currentX - startX; // 添加过渡效果 activeCard.style.transition = 'transform 0.5s ease, opacity 0.5s ease'; // 从数据集获取实际卡片类型,而不是依赖传递的参数 const cardType = activeCard.dataset.cardType; const isCardEvent = cardType === 'event'; // 检查是否为死亡卡片 const isDeathCard = activeCard.classList.contains('death-card'); // 如果是死亡卡片,直接调用swipeDeathCard if (isDeathCard) { const characterInfo = document.querySelector('.character-info'); swipeDeathCard(activeCard, characterInfo, activeCard.dataset.callback); gameState.isDragging = false; return; } // 处理正常卡片选择 if (deltaX < -100) { // 左滑 - 选择左侧选项 if (isCardEvent && gameState.eventNode) { selectEventChoice(0); // 事件选项0 } else if (gameState.currentEvent) { selectChoice('left'); // 普通卡片左选项 } else { // 恢复原位 resetCardPosition(); } } else if (deltaX > 100) { // 右滑 - 选择右侧选项 if (isCardEvent && gameState.eventNode) { // 对于事件卡片,检查是否有第二个选项 const optionIndex = gameState.eventNode.options.length > 1 ? 1 : 0; selectEventChoice(optionIndex); } else if (gameState.currentEvent) { selectChoice('right'); // 普通卡片右选项 } else { // 恢复原位 resetCardPosition(); } } else { // 回到原位 resetCardPosition(); } // 移除全局事件监听 document.removeEventListener('mousemove', onCardDragMove); document.removeEventListener('touchmove', onCardDragMove); document.removeEventListener('mouseup', onCardDragEnd); document.removeEventListener('touchend', onCardDragEnd); } // 重置卡片位置 function resetCardPosition() { if (!activeCard) return; activeCard.style.transform = 'translateX(0) rotate(0)'; // 隐藏选项覆盖层 const rightOption = activeCard.querySelector('.right-option-overlay'); const leftOption = activeCard.querySelector('.left-option-overlay'); if (rightOption) rightOption.style.opacity = 0; if (leftOption) leftOption.style.opacity = 0; // 移除效果指示器 removeAllEffectIndicators(); // 重置拖动状态 gameState.isDragging = false; } // 选择事件选项 function selectEventChoice(optionIndex) { if (!gameState.activeEvent || !gameState.eventNode) { console.error('当前事件或节点数据丢失'); resetCardPosition(); return; } // 获取角色信息元素 const characterInfo = document.querySelector('.character-info'); // 选择飞出方向,左滑(选项0)向左,右滑(选项1)向右 if (optionIndex === 0) { activeCard.style.transform = 'translateX(-1000px) rotate(-30deg)'; } else { activeCard.style.transform = 'translateX(1000px) rotate(30deg)'; } // 淡出并移除 activeCard.style.opacity = '0'; // 同时淡出角色信息和对话气泡 if (characterInfo) { characterInfo.style.opacity = '0'; } hideSpeechBubble(); // 移除效果指示器 removeAllEffectIndicators(); // 重置拖动状态 gameState.isDragging = false; // 延迟后处理选项效果和显示下一张卡片 setTimeout(() => { if (activeCard && activeCard.parentNode) { activeCard.remove(); activeCard = null; } if (characterInfo && characterInfo.parentNode) { characterInfo.remove(); } // 继续事件流程 continueEvent(optionIndex); }, 500); } // 选择一个选项 function selectChoice(direction) { if (!gameState.currentEvent || !gameState.currentCharacter) { console.error('当前事件或角色数据丢失'); resetCardPosition(); return; } // 应用效果 const effects = direction === 'left' ? gameState.currentEvent.optionA.effects : gameState.currentEvent.optionB.effects; applyEffects(effects); // 获取角色信息元素 const characterInfo = document.querySelector('.character-info'); // 向左或向右飞出 if (direction === 'left') { activeCard.style.transform = 'translateX(-1000px) rotate(-30deg)'; } else { activeCard.style.transform = 'translateX(1000px) rotate(30deg)'; } // 淡出并移除 activeCard.style.opacity = '0'; // 同时淡出角色信息和对话气泡 if (characterInfo) { characterInfo.style.opacity = '0'; } hideSpeechBubble(); // 移除效果指示器 removeAllEffectIndicators(); // 重置拖动状态 gameState.isDragging = false; // 延迟后显示下一张卡片 setTimeout(() => { if (activeCard && activeCard.parentNode) { activeCard.remove(); activeCard = null; } if (characterInfo && characterInfo.parentNode) { characterInfo.remove(); } // 重置当前事件和角色 gameState.currentEvent = null; gameState.currentCharacter = null; // 检查游戏是否结束 if (!gameState.gameOver) { // 应用状态效果 applyStatusEffects(); // 更新状态持续时间 updateStatuses(); // 检查是否应该触发事件 if (gameState.devMode && gameState.nextEventId) { startEvent(gameState.nextEventId); gameState.nextEventId = null; } else { const eventToTrigger = checkEventTriggers(); if (eventToTrigger) { startEvent(eventToTrigger); } else { // 翻转牌堆顶部的新牌 flipTopCard(); } } } }, 500); } // 应用效果 function applyEffects(effects) { // 更新统计数据 for (const [stat, value] of Object.entries(effects)) { gameState.stats[stat] = Math.max(0, Math.min(100, gameState.stats[stat] + value)); } // 增加年份和统治时间 gameState.year++; gameState.reignTime++; // 更新UI updateUI(); // 检查游戏结束条件 checkGameOver(); // 每10年保存一次游戏状态 if (gameState.reignTime % 10 === 0) { saveGameState(); } } // 应用状态效果 function applyStatusEffects() { if (gameState.activeStatuses.length === 0) return; // 记录效果总和 const totalEffects = { loyalty: 0, chaos: 0, population: 0, military: 0, resources: 0 }; // 收集所有状态的每回合效果 gameState.activeStatuses.forEach(status => { if (status.effects_per_turn) { for (const [stat, value] of Object.entries(status.effects_per_turn)) { totalEffects[stat] += value; } } }); // 应用总效果 for (const [stat, value] of Object.entries(totalEffects)) { if (value !== 0) { gameState.stats[stat] = Math.max(0, Math.min(100, gameState.stats[stat] + value)); } } // 更新UI updateUI(); // 检查游戏结束条件 checkGameOver(); } // 更新状态持续时间 function updateStatuses() { // 更新状态持续时间并检查是否结束 gameState.activeStatuses = gameState.activeStatuses.filter(status => { // 减少持续时间 if (status.remainingDuration > 0) { status.remainingDuration--; } // 检查是否应该移除状态 if (status.remainingDuration === 0 && status.duration > 0) { // 状态持续时间结束 return false; } // 特定结束条件检查 if (status.one_time_save && status.save_trigger) { // 检查是否已经触发了一次性救援 if (status.triggered) { return false; } // 检查触发条件 if (status.save_trigger === 'military_zero' && gameState.stats.military <= 10) { // 军事力量接近0,触发救援 status.triggered = true; // 应用救援效果 gameState.stats.military += 30; // 显示救援消息 showSpeechBubble("阿斯塔特战士如约而至,解除了您的危机!"); // 状态仍然保留,直到下一回合被移除 return true; } } return true; }); // 更新状态显示 updateStatusDisplay(); } // 添加状态 function addStatus(statusId) { if (!gameData || !gameData.statuses) return; // 查找状态数据 const statusData = gameData.statuses.find(s => s.id === statusId); if (!statusData) return; // 检查状态是否已经存在 const existingIndex = gameState.activeStatuses.findIndex(s => s.id === statusId); if (existingIndex >= 0) { // 已存在,重置持续时间 if (statusData.duration > 0) { gameState.activeStatuses[existingIndex].remainingDuration = statusData.duration; } return; } // 创建新状态对象 const newStatus = { id: statusData.id, name: statusData.name, icon: statusData.icon, description: statusData.description, effects_per_turn: statusData.effects_per_turn, one_time_save: statusData.one_time_save, save_trigger: statusData.save_trigger, startYear: gameState.year, triggered: false }; // 设置持续时间 if (statusData.duration > 0) { newStatus.remainingDuration = statusData.duration; newStatus.duration = statusData.duration; } // 添加到活跃状态列表 gameState.activeStatuses.push(newStatus); // 更新状态显示 updateStatusDisplay(); } // 移除状态 function removeStatus(statusId) { // 从活跃状态列表中移除指定ID的状态 gameState.activeStatuses = gameState.activeStatuses.filter(s => s.id !== statusId); // 更新状态显示 updateStatusDisplay(); } // 更新状态显示 function updateStatusDisplay() { // 清空状态图标容器 activeStatusesContainer.innerHTML = ''; // 添加所有活跃状态的图标 gameState.activeStatuses.forEach(status => { const statusItem = document.createElement('div'); statusItem.className = 'status-item'; statusItem.textContent = status.icon; statusItem.dataset.statusId = status.id; statusItem.dataset.tooltip = status.name; // 如果是一次性救援状态且满足触发条件,添加警告效果 if (status.one_time_save && status.save_trigger === 'military_zero' && gameState.stats.military <= 15) { statusItem.classList.add('warning'); } // 点击显示详情 statusItem.addEventListener('click', () => { showStatusDetails(status); }); // 添加到容器 activeStatusesContainer.appendChild(statusItem); }); } // 显示状态详情 function showStatusDetails(status) { // 设置详情对话框内容 document.getElementById('status-details-icon').textContent = status.icon; document.getElementById('status-details-name').textContent = status.name; document.getElementById('status-details-description').textContent = status.detailed_description || status.description; // 显示效果 const effectsContainer = document.getElementById('status-details-effects'); effectsContainer.innerHTML = ''; if (status.effects_per_turn) { const effectsTitle = document.createElement('div'); effectsTitle.textContent = '每回合效果:'; effectsTitle.style.marginBottom = '8px'; effectsContainer.appendChild(effectsTitle); for (const [stat, value] of Object.entries(status.effects_per_turn)) { if (value === 0) continue; const effectItem = document.createElement('div'); effectItem.className = 'status-effect-item'; let statName = ''; let statIcon = ''; switch (stat) { case 'loyalty': statName = '帝国忠诚度'; statIcon = '🦅'; break; case 'chaos': statName = '混沌侵染'; statIcon = '🌀'; break; case 'population': statName = '民众控制'; statIcon = '👥'; break; case 'military': statName = '军事力量'; statIcon = '🔫'; break; case 'resources': statName = '资源储备'; statIcon = '⚙️'; break; } effectItem.innerHTML = ` ${statIcon} ${statName} ${value > 0 ? '+' : ''}${value} `; effectsContainer.appendChild(effectItem); } } else if (status.one_time_save) { const effectsInfo = document.createElement('div'); effectsInfo.textContent = '当军事力量接近为0时,将提供一次性救援。'; effectsContainer.appendChild(effectsInfo); } else { const noEffectsInfo = document.createElement('div'); noEffectsInfo.textContent = '此状态没有直接的数值效果。'; effectsContainer.appendChild(noEffectsInfo); } // 显示持续时间 const durationElement = document.getElementById('status-details-duration'); if (status.duration) { durationElement.textContent = `持续时间: ${status.remainingDuration}/${status.duration} 年`; } else if (status.one_time_save) { durationElement.textContent = '持续到触发一次救援'; } else { durationElement.textContent = '持续时间: 无限期'; } // 显示对话框 statusDetailsDialog.classList.add('show'); } // 关闭状态详情对话框 closeStatusDetailsButton.addEventListener('click', () => { statusDetailsDialog.classList.remove('show'); }); // 开发者工具按钮点击 devToolsButton.addEventListener('click', () => { devToolsDialog.classList.add('show'); }); // 关闭开发者工具对话框 closeDevToolsButton.addEventListener('click', () => { devToolsDialog.classList.remove('show'); }); // 触发事件按钮点击 triggerEventButton.addEventListener('click', () => { const selectedEventId = eventSelector.value; if (!selectedEventId) return; // 设置下一个要触发的事件 gameState.nextEventId = selectedEventId; // 关闭对话框 devToolsDialog.classList.remove('show'); // 如果当前有活动卡片,则移除它 if (activeCard) { // 获取角色信息元素 const characterInfo = document.querySelector('.character-info'); // 淡出并移除当前卡片 activeCard.style.transition = 'all 0.3s ease'; activeCard.style.opacity = '0'; activeCard.style.transform = 'scale(0.8)'; if (characterInfo) { characterInfo.style.opacity = '0'; } hideSpeechBubble(); // 延迟后移除元素并开始新事件 setTimeout(() => { if (activeCard && activeCard.parentNode) { activeCard.remove(); activeCard = null; } if (characterInfo && characterInfo.parentNode) { characterInfo.remove(); } // 立即开始事件 startEvent(selectedEventId); }, 300); } else { // 如果没有活动卡片,寻找一张背面牌进行翻转 const topBackCard = cardContainer.querySelector('.card-back:not(.static-background-card)'); if (topBackCard) { startEvent(selectedEventId); } } }); // 添加状态按钮点击 addStatusButton.addEventListener('click', () => { const selectedStatusId = statusSelector.value; if (!selectedStatusId) return; // 添加状态 addStatus(selectedStatusId); // 关闭对话框 devToolsDialog.classList.remove('show'); }); // 移除状态按钮点击 removeStatusButton.addEventListener('click', () => { const selectedStatusId = statusSelector.value; if (!selectedStatusId) return; // 移除状态 removeStatus(selectedStatusId); // 关闭对话框 devToolsDialog.classList.remove('show'); }); // 设置属性值按钮点击 setStatButton.addEventListener('click', () => { const selectedStat = statSelector.value; const value = parseInt(statValueInput.value); if (!selectedStat || isNaN(value) || value < 0 || value > 100) return; // 设置属性值 gameState.stats[selectedStat] = value; // 更新UI updateUI(); // 检查游戏结束条件 checkGameOver(); // 关闭对话框 devToolsDialog.classList.remove('show'); }); // 更新UI function updateUI() { // 更新年份和统治时间 reignYearsElement.textContent = gameState.reignTime; currentYearElement.textContent = gameState.year.toLocaleString(); // 更新状态条 for (const [stat, bar] of Object.entries(statusBars)) { bar.style.width = `${gameState.stats[stat]}%`; // 根据状态值改变颜色 if (gameState.stats[stat] < 20) { bar.style.opacity = '0.7'; // 危险状态时降低透明度 } else if (gameState.stats[stat] > 80) { bar.style.opacity = '0.7'; // 危险状态时降低透明度 } else { bar.style.opacity = '1'; } } // 更新状态图标 updateStatusDisplay(); } // 检查游戏结束条件 function checkGameOver() { // 检查各项指标是否达到极值 const endTypes = { loyalty: { low: "loyalty_low", high: "loyalty_high" }, chaos: { low: "chaos_low", high: "chaos_high" }, population: { low: "population_low", high: "population_high" }, military: { low: "military_low", high: "military_high" }, resources: { low: "resources_low", high: "resources_high" } }; // 检查是否有阿斯塔特的守护承诺 const hasAstartesProtection = gameState.activeStatuses.some(s => s.id === 'astartes_protection' && !s.triggered); // 检查每个指标 for (const [stat, types] of Object.entries(endTypes)) { const value = gameState.stats[stat]; // 特殊处理军事力量属性,如果有阿斯塔特保护且未触发,不因为军事力量低结束游戏 if (stat === 'military' && value <= 10 && hasAstartesProtection) { // 触发阿斯塔特保护效果,而不是结束游戏 let astartes = gameState.activeStatuses.find(s => s.id === 'astartes_protection'); if (astartes) { astartes.triggered = true; gameState.stats.military += 30; updateUI(); showSpeechBubble("阿斯塔特战士如约而至,解除了您的危机!"); setTimeout(() => { hideSpeechBubble(); }, 3000); continue; } } if (value <= 1) { showDeathScenario(types.low); return; } else if (value >= 99) { showDeathScenario(types.high); return; } } } // 新增:显示死亡场景 function showDeathScenario(deathType) { if (gameState.gameOver) return; // 防止多次触发 // 标记游戏已结束,但不立即显示游戏结束对话框 gameState.gameOver = true; console.log(`尝试获取死亡场景: /api/death_scenario/${deathType}`); // 从服务器获取随机死亡场景 fetch(`/api/death_scenario/${deathType}`) .then(response => { console.log('API响应状态:', response.status); if (!response.ok) { throw new Error(`API请求失败: ${response.status}`); } return response.json(); }) .then(scenario => { console.log('成功获取死亡场景:', scenario); // 保存死亡原因,后面显示游戏结束对话框时使用 gameState.gameOverReason = scenario.name; // 显示第一张死亡卡片 showDeathCard(scenario.first_card, () => { // 第一张卡片结束后,显示第二张 showDeathCard(scenario.second_card, () => { // 第二张卡片结束后,显示游戏结束对话框 finalizeGameOver(); }); }); }) .catch(error => { console.error('获取死亡场景失败:', error); endGameWithMessage('游戏结束'); // 如果获取失败,使用简单的结束信息 }); } // 显示死亡卡片 function showDeathCard(cardData, callback) { // 获取角色数据 const character = cardData.character; // 清理现有卡片和信息 const existingFrontCard = document.getElementById('active-card'); if (existingFrontCard) { existingFrontCard.remove(); } const existingCharInfo = document.querySelector('.character-info'); if (existingCharInfo) { existingCharInfo.remove(); } // 创建角色信息区域 const characterInfo = document.createElement('div'); characterInfo.className = 'character-info'; characterInfo.innerHTML = `
${character.name}
${character.title}
`; characterInfo.style.opacity = '0'; characterInfo.style.zIndex = '20'; // 检查角色是否有图片路径 const hasImage = character.avatar_path && character.avatar_path.length > 0; const portraitContent = hasImage ? `${character.name}` : `
${character.avatar}
`; // 创建死亡卡片 const deathCard = document.createElement('div'); deathCard.className = 'card death-card'; deathCard.id = 'active-card'; deathCard.style.zIndex = '15'; // 存储回调函数,供后续使用 deathCard.dataset.callback = "death_callback_" + Math.random(); window[deathCard.dataset.callback] = callback; // 设置卡片内容 - 包括选项 deathCard.innerHTML = `
${portraitContent}
${cardData.options[0].text}
${cardData.options[1] ? cardData.options[1].text : cardData.options[0].text}
`; // 添加到DOM cardContainer.appendChild(characterInfo); cardContainer.appendChild(deathCard); // 设置卡片初始样式 deathCard.style.transform = 'rotateY(-90deg)'; deathCard.style.opacity = '0'; deathCard.style.transition = 'none'; // 强制重绘 void deathCard.offsetWidth; // 寻找一张背面牌用于翻转效果 const topBackCard = cardContainer.querySelector('.card-back:not(.static-background-card)'); if (topBackCard) { // 背面牌开始翻转 topBackCard.style.transition = 'all 0.4s ease'; topBackCard.style.transform = 'rotateY(90deg)'; topBackCard.style.opacity = '0'; topBackCard.style.zIndex = '5'; } // 等待背面牌翻转到一半后,开始前面牌的翻转 setTimeout(() => { // 前面卡片开始翻转 deathCard.style.transition = 'all 0.4s ease'; deathCard.style.transform = 'rotateY(0deg)'; deathCard.style.opacity = '1'; // 设置为当前活动卡片 activeCard = deathCard; // 显示对话气泡 showSpeechBubble(cardData.text); // 显示角色信息 setTimeout(() => { characterInfo.style.opacity = '1'; // 如果第二张卡片没有选项文本,自动划过 if (cardData.options[0].text === '' && (cardData.options.length < 2 || cardData.options[1].text === '')) { // 延迟自动划过,给玩家时间阅读卡片 setTimeout(() => { swipeDeathCard(deathCard, characterInfo, callback); }, 3000); } else { // 添加卡片点击或拖动事件 setupDeathCardInteraction(deathCard, characterInfo, callback); } // 移除原来的背面牌 if (topBackCard && topBackCard.parentNode) { topBackCard.remove(); } }, 400); }, 200); } // 设置死亡卡片交互 function setupDeathCardInteraction(card, charInfo, callback) { // 点击卡片触发划过 card.addEventListener('click', () => { swipeDeathCard(card, charInfo, callback); }); // 也可以使用拖动 setupCardDrag(card); } // 划过死亡卡片 function swipeDeathCard(card, charInfo, callback) { // 向右划出 card.style.transition = 'all 0.5s ease'; card.style.transform = 'translateX(1000px) rotate(30deg)'; card.style.opacity = '0'; // 隐藏角色信息和对话气泡 if (charInfo) { charInfo.style.opacity = '0'; } hideSpeechBubble(); // 等待动画完成后执行回调 setTimeout(() => { if (card && card.parentNode) { card.remove(); } if (charInfo && charInfo.parentNode) { charInfo.remove(); } // 执行回调 if (callback && typeof callback === 'function') { callback(); } else if (card.dataset.callback && window[card.dataset.callback]) { window[card.dataset.callback](); // 清理回调引用 delete window[card.dataset.callback]; } }, 500); } // 使用特定消息结束游戏 function endGameWithMessage(message) { gameState.gameOver = true; gameState.gameOverReason = message; finalizeGameOver(); } // 完成游戏结束流程 function finalizeGameOver() { // 更新高分 updateHighScore(); // 显示游戏结束对话框 gameOverReason.textContent = gameState.gameOverReason; finalYears.textContent = gameState.reignTime; gameOverDialog.classList.add('show'); } // 更新高分 function updateHighScore() { fetch('/api/update_score', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ score: gameState.reignTime, year: gameState.year // 发送当前年份以便服务器保存 }) }) .catch(error => console.error('更新分数失败:', error)); } // 保存游戏状态 function saveGameState() { // 不保存已结束的游戏 if (gameState.gameOver) return; // 创建要保存的游戏状态副本 const gameStateCopy = { governor: gameState.governor, reignTime: gameState.reignTime, year: gameState.year, // 保存当前年份 stats: { ...gameState.stats }, statusEffects: [...gameState.statusEffects], activeStatuses: [...gameState.activeStatuses], completedEvents: [...gameState.completedEvents], // 不保存临时状态 gameOver: false, gameOverReason: '' // 不要在这里设置achievements字段,避免覆盖服务器上已有的成就 }; console.log("保存游戏状态..."); fetch('/api/save_game', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(gameStateCopy) }) .then(response => { if (!response.ok) { throw new Error('保存游戏状态失败: ' + response.status); } return response.json(); }) .then(data => { console.log("游戏状态保存成功"); }) .catch(error => { console.error('保存游戏状态失败:', error); }); } // 重启游戏按钮事件 restartButton.addEventListener('click', () => { // 隐藏游戏结束对话框 gameOverDialog.classList.remove('show'); // 重新初始化游戏 setTimeout(initGame, 500); }); // 在游戏加载时初始化 document.addEventListener('DOMContentLoaded', () => { // 公告横幅关闭按钮 const announcementBanner = document.querySelector('.announcement-banner'); const closeAnnouncementBtn = document.querySelector('.announcement-close'); if (closeAnnouncementBtn && announcementBanner) { closeAnnouncementBtn.addEventListener('click', () => { announcementBanner.style.display = 'none'; }); } // 原有的初始化代码 loadGameData(); }); // 当窗口关闭或刷新时保存游戏状态 window.addEventListener('beforeunload', () => { if (!gameState.gameOver) { saveGameState(); } });