// 游戏状态
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.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.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.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();
}
});