diff --git a/game.js b/game.js index 5d7ad37..3f56f24 100644 --- a/game.js +++ b/game.js @@ -211,6 +211,14 @@ class Card { }; return ranks[this.rank] || this.rank; } + + isRed() { + return this.suit === 'heart' || this.suit === 'diamond'; + } + + isBlack() { + return this.suit === 'spade' || this.suit === 'club'; + } } class Player { @@ -583,13 +591,13 @@ class Game { this.useSunQuanSkill(); break; case '关羽': - this.addLog('武圣技能:红色手牌自动当作杀使用'); + this.addLog('武圣技能:出牌时红色手牌可以当作杀使用'); break; case '张飞': this.addLog('咆哮技能:出牌阶段可以使用任意张杀'); break; case '赵云': - this.addLog('龙胆技能:杀闪互换自动触发'); + this.addLog('龙胆技能:杀可以当闪使用,闪可以当杀使用'); break; case '诸葛亮': this.useZhugeLiangSkill(); @@ -802,8 +810,106 @@ class Game { player.showHealEffect(); this.addLog(`${player.character.name} 使用了【桃】,回复1点体力`); } else if (card.type === 'dodge') { - this.addLog('闪只能在对方使用杀时使用'); - return false; + if (player.character.name === '赵云') { + if (this.attackCount >= 1 && !this.hasWeaponWithEffect('zhugeliannu')) { + this.addLog('每回合只能使用一张杀'); + return false; + } + + if (targetIndex === -1) { + if (this.selectedTargetIndex !== -1) { + targetIndex = this.selectedTargetIndex; + } else { + targetIndex = this.getNextAlivePlayer(this.currentPlayerIndex); + } + } + + const target = this.players[targetIndex]; + if (!target.isAlive) { + this.addLog('目标已阵亡'); + return false; + } + + if (!this.isInRange(targetIndex)) { + this.addLog('目标不在攻击范围内'); + return false; + } + + this.addLog(`${player.character.name} 发动【龙胆】,将【闪】当作【杀】使用`); + this.addLog(`${player.character.name} 对 ${target.character.name} 使用了【杀】`); + this.showAttackAnimation(targetIndex); + + if (this.useDodge(target, player)) { + this.addLog(`${target.character.name} 使用了【闪】`); + } else { + target.takeDamage(1); + target.showDamageEffect(); + this.addLog(`${target.character.name} 受到1点伤害`); + + if (target.statusEffects.chain) { + this.handleChainDamage(target, ''); + } + + this.checkDeath(target); + } + + this.attackCount++; + } else { + this.addLog('闪只能在对方使用杀时使用'); + return false; + } + } else if (player.character.name === '关羽' && card.isRed()) { + if (this.attackCount >= 1 && !this.hasWeaponWithEffect('zhugeliannu')) { + this.addLog('每回合只能使用一张杀'); + return false; + } + + if (targetIndex === -1) { + if (this.selectedTargetIndex !== -1) { + targetIndex = this.selectedTargetIndex; + } else { + targetIndex = this.getNextAlivePlayer(this.currentPlayerIndex); + } + } + + const target = this.players[targetIndex]; + if (!target.isAlive) { + this.addLog('目标已阵亡'); + return false; + } + + if (!this.isInRange(targetIndex)) { + this.addLog('目标不在攻击范围内'); + return false; + } + + this.addLog(`${player.character.name} 发动【武圣】,将【${card.name}】当作【杀】使用`); + this.addLog(`${player.character.name} 对 ${target.character.name} 使用了【杀】`); + this.showAttackAnimation(targetIndex); + + let damage = 1; + let needsDodge = true; + + if (target.equipment.armor && target.equipment.armor.name === '藤甲') { + needsDodge = false; + this.addLog(`${target.character.name} 的藤甲使普通杀无效`); + } + + if (needsDodge && this.useDodge(target, player)) { + this.addLog(`${target.character.name} 使用了【闪】`); + } else { + target.takeDamage(damage); + target.showDamageEffect(); + this.addLog(`${target.character.name} 受到${damage}点伤害`); + + if (target.statusEffects.chain) { + this.handleChainDamage(target, ''); + } + + this.checkDeath(target); + } + + this.attackCount++; } const playedCard = player.removeCard(cardIndex); @@ -906,6 +1012,16 @@ class Game { } } + } else if (card.type === 'barbarian') { + this.addLog(`${player.character.name} 使用了【南蛮入侵】`); + this.showCardUseAnimation(); + this.handleBarbarian(player); + + } else if (card.type === 'arrow') { + this.addLog(`${player.character.name} 使用了【万箭齐发】`); + this.showCardUseAnimation(); + this.handleArrow(player); + } else if (card.type === 'nullify') { this.addLog('无懈可击需要在锦囊牌使用时使用'); return false; @@ -1074,13 +1190,21 @@ class Game { let dodgesUsed = 0; for (let i = 0; i < dodgeCount; i++) { - const dodgeIndex = target.hand.findIndex(c => c.type === 'dodge'); + let dodgeIndex = target.hand.findIndex(c => c.type === 'dodge'); + + if (target.character.name === '赵云' && dodgeIndex === -1) { + dodgeIndex = target.hand.findIndex(c => c.type === 'attack'); + if (dodgeIndex !== -1) { + this.addLog(`${target.character.name} 发动【龙胆】,将【杀】当作【闪】使用`); + } + } + if (dodgeIndex !== -1) { if (target.isHuman) { dodgesUsed++; } else { - target.removeCard(dodgeIndex); - this.discardCard(target.hand[dodgeIndex]); + const dodgeCard = target.removeCard(dodgeIndex); + this.discardCard(dodgeCard); dodgesUsed++; } this.showShieldAnimation(target.index); @@ -1109,14 +1233,14 @@ class Game { while (true) { const attackIndex = currentAttacker.hand.findIndex(c => c.type === 'attack'); if (attackIndex !== -1) { - currentAttacker.removeCard(attackIndex); - this.discardCard(currentAttacker.hand[attackIndex]); + const attackCard = currentAttacker.removeCard(attackIndex); + this.discardCard(attackCard); this.showAttackAnimation(currentDefender.index); const defendIndex = currentDefender.hand.findIndex(c => c.type === 'attack'); if (defendIndex !== -1) { - currentDefender.removeCard(defendIndex); - this.discardCard(currentDefender.hand[defendIndex]); + const defendCard = currentDefender.removeCard(defendIndex); + this.discardCard(defendCard); [currentAttacker, currentDefender] = [currentDefender, currentAttacker]; } else { currentDefender.takeDamage(1); @@ -1131,6 +1255,63 @@ class Game { } } + handleBarbarian(attacker) { + const targets = this.players.filter(p => p.isAlive && p.index !== attacker.index); + for (const target of targets) { + let attackIndex = target.hand.findIndex(c => c.type === 'attack'); + + if (attackIndex === -1 && target.character.name === '关羽') { + attackIndex = target.hand.findIndex(c => c.isRed()); + if (attackIndex !== -1) { + this.addLog(`${target.character.name} 发动【武圣】,将【${target.hand[attackIndex].name}】当作【杀】使用`); + } + } + + if (attackIndex === -1 && target.character.name === '赵云') { + attackIndex = target.hand.findIndex(c => c.type === 'dodge'); + if (attackIndex !== -1) { + this.addLog(`${target.character.name} 发动【龙胆】,将【闪】当作【杀】使用`); + } + } + + if (attackIndex !== -1) { + const attackCard = target.removeCard(attackIndex); + this.discardCard(attackCard); + this.addLog(`${target.character.name} 打出了【杀】`); + } else { + target.takeDamage(1); + target.showDamageEffect(); + this.addLog(`${target.character.name} 受到1点伤害`); + this.checkDeath(target); + } + } + } + + handleArrow(attacker) { + const targets = this.players.filter(p => p.isAlive && p.index !== attacker.index); + for (const target of targets) { + let dodgeIndex = target.hand.findIndex(c => c.type === 'dodge'); + + if (dodgeIndex === -1 && target.character.name === '赵云') { + dodgeIndex = target.hand.findIndex(c => c.type === 'attack'); + if (dodgeIndex !== -1) { + this.addLog(`${target.character.name} 发动【龙胆】,将【杀】当作【闪】使用`); + } + } + + if (dodgeIndex !== -1) { + const dodgeCard = target.removeCard(dodgeIndex); + this.discardCard(dodgeCard); + this.addLog(`${target.character.name} 打出了【闪】`); + } else { + target.takeDamage(1); + target.showDamageEffect(); + this.addLog(`${target.character.name} 受到1点伤害`); + this.checkDeath(target); + } + } + } + startDuel(player1, player2) { this.handleDuel(player1, player2); } @@ -1312,85 +1493,273 @@ class Game { aiPlayCards(player) { const targets = this.players.filter(p => p.isAlive && p.index !== player.index); - const canUseMultipleAttacks = this.hasWeaponWithEffect('zhugeliannu') || player.character.name === '张飞'; - while (true) { - const attackCard = player.hand.findIndex(c => c.type === 'attack'); - if (attackCard !== -1 && (this.attackCount === 0 || canUseMultipleAttacks)) { - const target = this.aiSelectTarget(player, targets); - if (target !== -1) { - this.playCard(attackCard, target); - updateUI(); - continue; + const cardPriority = [ + { type: 'peach', condition: () => player.currentHp < player.maxHp, target: null }, + { type: 'attack', condition: () => this.attackCount === 0 || canUseMultipleAttacks, target: 'selectTarget', skillCheck: true }, + { type: 'duel', condition: () => true, target: 'selectDuelTarget' }, + { type: 'barbarian', condition: () => true, target: null }, + { type: 'arrow', condition: () => true, target: null }, + { type: 'fireAttack', condition: () => true, target: 'selectFireAttackTarget' }, + { type: 'dismantlement', condition: () => true, target: 'selectDismantleTarget' }, + { type: 'steal', condition: () => true, target: 'selectStealTarget' }, + { type: 'chain', condition: () => true, target: 'selectChainTarget' }, + { type: 'happy', condition: () => true, target: 'selectHappyTarget' }, + { type: 'starvation', condition: () => true, target: 'selectStarvationTarget' }, + { type: 'lightning', condition: () => true, target: 'selectLightningTarget' }, + { type: 'draw', condition: () => true, target: null }, + { type: 'equip', condition: () => true, target: 'selectEquipCard', isEquip: true } + ]; + + const playNextCard = () => { + let cardsUsed = false; + + if (player.character.name === '刘备' && player.hand.length >= 2) { + const ally = targets.find(t => t.identity === 'loyalist' || t.identity === 'lord'); + if (ally && ally.currentHp < ally.maxHp) { + const cardIndex = player.hand.findIndex(c => c.type === 'peach'); + if (cardIndex !== -1) { + this.useLiuBeiSkillAI(player, ally, cardIndex); + cardsUsed = true; + } } } - break; - } - - const peachCard = player.hand.findIndex(c => c.type === 'peach'); - if (peachCard !== -1 && player.currentHp < player.maxHp) { - this.playCard(peachCard); - updateUI(); - } - - const dismantleCard = player.hand.findIndex(c => c.type === 'dismantlement'); - if (dismantleCard !== -1) { - const target = this.aiSelectTarget(player, targets); - if (target !== -1) { - this.playCard(dismantleCard, target); - updateUI(); + + if (!cardsUsed && player.character.name === '孙权' && player.hand.length >= 2) { + const uselessCards = player.hand.filter(c => c.type !== 'attack' && c.type !== 'peach' && c.type !== 'dodge'); + if (uselessCards.length > 0) { + this.useSunQuanSkillAI(player, uselessCards[0]); + cardsUsed = true; + } } - } - - const stealCard = player.hand.findIndex(c => c.type === 'steal'); - if (stealCard !== -1) { - const target = this.aiSelectTarget(player, targets); - if (target !== -1) { - this.playCard(stealCard, target); - updateUI(); + + if (!cardsUsed && player.character.name === '貂蝉') { + const malePlayers = targets.filter(t => t.character.name !== '貂蝉'); + if (malePlayers.length >= 2) { + const target1 = malePlayers[0]; + const target2 = malePlayers[1]; + this.useDiaoChanSkillAI(player, target1, target2); + cardsUsed = true; + } } - } + + if (!cardsUsed) { + for (const priority of cardPriority) { + if (cardsUsed) break; + + let cardIndex = -1; + + if (priority.isEquip) { + cardIndex = this[priority.target](player); + } else { + cardIndex = player.hand.findIndex(c => c.type === priority.type); + + if (priority.skillCheck && cardIndex === -1) { + if (player.character.name === '关羽') { + cardIndex = player.hand.findIndex(c => c.isRed()); + if (cardIndex !== -1) { + this.addLog(`${player.character.name} 发动【武圣】,将【${player.hand[cardIndex].name}】当作【杀】使用`); + } + } else if (player.character.name === '赵云') { + cardIndex = player.hand.findIndex(c => c.type === 'dodge'); + if (cardIndex !== -1) { + this.addLog(`${player.character.name} 发动【龙胆】,将【闪】当作【杀】使用`); + } + } + } + } + + if (cardIndex !== -1 && priority.condition()) { + let targetIndex = -1; + + if (priority.target && !priority.isEquip) { + targetIndex = this[priority.target](player, targets); + } + + if (targetIndex !== -1 || !priority.target) { + this.playCard(cardIndex, targetIndex); + cardsUsed = true; + } + } + } + } + + if (cardsUsed) { + updateUI(); + setTimeout(playNextCard, 300); + } else { + updateUI(); + setTimeout(() => { + this.endTurn(); + updateUI(); + }, 300); + } + }; - const drawCard = player.hand.findIndex(c => c.type === 'draw'); - if (drawCard !== -1) { - this.playCard(drawCard); - updateUI(); - } - - const equipCard = player.hand.findIndex(c => c.category === 'equip'); - if (equipCard !== -1) { - this.playCard(equipCard); - updateUI(); - } - - setTimeout(() => { - this.endTurn(); - updateUI(); - }, 300); + playNextCard(); } aiSelectTarget(player, targets) { + const inRangeTargets = targets.filter(t => this.isInRange(t.index)); + + if (inRangeTargets.length === 0) { + return -1; + } + if (player.identity === 'lord') { - const rebel = targets.find(t => t.identity === 'rebel'); + const rebel = inRangeTargets.find(t => t.identity === 'rebel'); if (rebel) return rebel.index; - const spy = targets.find(t => t.identity === 'spy'); + const spy = inRangeTargets.find(t => t.identity === 'spy'); if (spy) return spy.index; } else if (player.identity === 'loyalist') { - const rebel = targets.find(t => t.identity === 'rebel'); + const rebel = inRangeTargets.find(t => t.identity === 'rebel'); if (rebel) return rebel.index; } else if (player.identity === 'rebel') { - const lord = targets.find(t => t.identity === 'lord'); + const lord = inRangeTargets.find(t => t.identity === 'lord'); if (lord) return lord.index; - const loyalist = targets.find(t => t.identity === 'loyalist'); + const loyalist = inRangeTargets.find(t => t.identity === 'loyalist'); if (loyalist) return loyalist.index; } else if (player.identity === 'spy') { - const weakTarget = targets.reduce((weakest, t) => - t.currentHp < weakest.currentHp ? t : weakest, targets[0]); + const weakTarget = inRangeTargets.reduce((weakest, t) => + t.currentHp < weakest.currentHp ? t : weakest, inRangeTargets[0]); if (weakTarget) return weakTarget.index; } - return targets.length > 0 ? targets[0].index : -1; + return inRangeTargets.length > 0 ? inRangeTargets[0].index : -1; + } + + useLiuBeiSkillAI(player, ally, cardIndex) { + const card = player.removeCard(cardIndex); + ally.addCard(card); + this.addLog(`${player.character.name} 发动【仁德】,将【${card.name}】赠予 ${ally.character.name}`); + } + + useSunQuanSkillAI(player, card) { + const cardIndex = player.hand.indexOf(card); + if (cardIndex !== -1) { + player.removeCard(cardIndex); + this.discardCard(card); + this.addLog(`${player.character.name} 发动【制衡】,弃置了【${card.name}】`); + + const newCard = this.drawCard(); + if (newCard) { + player.addCard(newCard); + this.addLog(`${player.character.name} 摸了一张牌`); + } + } + } + + useDiaoChanSkillAI(player, target1, target2) { + this.addLog(`${player.character.name} 发动【离间】,让 ${target1.character.name} 和 ${target2.character.name} 决斗`); + this.startDuel(target1, target2); + } + + aiSelectDuelTarget(player, targets) { + const weakTargets = targets.filter(t => t.hand.length < 2); + if (weakTargets.length > 0) { + return this.aiSelectTarget(player, weakTargets); + } + return this.aiSelectTarget(player, targets); + } + + aiSelectFireAttackTarget(player, targets) { + const target = this.aiSelectTarget(player, targets); + if (target !== -1) { + const targetPlayer = this.players[target]; + const hasRedCard = targetPlayer.hand.some(c => c.suit === 'heart' || c.suit === 'diamond'); + if (!hasRedCard) { + return target; + } + } + return this.aiSelectTarget(player, targets); + } + + aiSelectDismantleTarget(player, targets) { + const targetWithCards = targets.filter(t => t.hand.length > 0); + if (targetWithCards.length > 0) { + return this.aiSelectTarget(player, targetWithCards); + } + return this.aiSelectTarget(player, targets); + } + + aiSelectStealTarget(player, targets) { + const targetWithCards = targets.filter(t => t.hand.length > 0); + if (targetWithCards.length > 0) { + return this.aiSelectTarget(player, targetWithCards); + } + return this.aiSelectTarget(player, targets); + } + + aiSelectChainTarget(player, targets) { + const unchainedTargets = targets.filter(t => !t.statusEffects.chain); + if (unchainedTargets.length > 0) { + return this.aiSelectTarget(player, unchainedTargets); + } + return this.aiSelectTarget(player, targets); + } + + aiSelectHappyTarget(player, targets) { + const dangerousTargets = targets.filter(t => t.hand.length >= 3); + if (dangerousTargets.length > 0) { + return this.aiSelectTarget(player, dangerousTargets); + } + return this.aiSelectTarget(player, targets); + } + + aiSelectStarvationTarget(player, targets) { + const inRangeTargets = targets.filter(t => this.isInRange(t.index)); + if (inRangeTargets.length > 0) { + return this.aiSelectTarget(player, inRangeTargets); + } + return -1; + } + + aiSelectLightningTarget(player, targets) { + const enemyTargets = targets.filter(t => { + if (player.identity === 'lord' || player.identity === 'loyalist') { + return t.identity === 'rebel' || t.identity === 'spy'; + } else if (player.identity === 'rebel') { + return t.identity === 'lord' || t.identity === 'loyalist'; + } else { + return true; + } + }); + + if (enemyTargets.length > 0) { + return this.aiSelectTarget(player, enemyTargets); + } + return this.aiSelectTarget(player, targets); + } + + aiSelectEquipCard(player) { + const equipCards = player.hand.filter(c => c.category === 'equip'); + if (equipCards.length === 0) return -1; + + for (const card of equipCards) { + const cardIndex = player.hand.indexOf(card); + + if (card.type === 'weapon') { + if (!player.equipment.weapon || + (player.equipment.weapon.name !== '诸葛连弩' && card.name === '诸葛连弩')) { + return cardIndex; + } + } else if (card.type === 'armor') { + if (!player.equipment.armor) { + return cardIndex; + } + } else if (card.type === 'horseMinus') { + if (!player.equipment.horseMinus) { + return cardIndex; + } + } else if (card.type === 'horsePlus') { + if (!player.equipment.horsePlus) { + return cardIndex; + } + } + } + + return player.hand.indexOf(equipCards[0]); } addLog(message, highlight = false) { @@ -1449,7 +1818,7 @@ function updateUI() { updateUITimer = setTimeout(() => { updateUIPending = false; renderUI(); - }, 16); + }, 50); } function renderUI() { @@ -1462,6 +1831,11 @@ function renderUI() { const attackRange = calculateAttackRange(player); document.getElementById('attack-range').textContent = `攻击距离: ${attackRange}`; + const isPlayPhase = game.phase === 'play'; + const hasSelectedCard = game.selectedCardIndex !== -1 && player.isHuman && isPlayPhase; + const selectedCard = hasSelectedCard ? player.hand[game.selectedCardIndex] : null; + const isAttackOrScroll = selectedCard && (selectedCard.type === 'attack' || selectedCard.category === 'scroll'); + for (let i = 0; i < 5; i++) { const p = game.players[i]; @@ -1482,16 +1856,11 @@ function renderUI() { playerCard.classList.add('active'); } - if (game.selectedCardIndex !== -1 && player.isHuman && game.phase === 'play') { - const selectedCard = player.hand[game.selectedCardIndex]; - if (selectedCard && (selectedCard.type === 'attack' || selectedCard.category === 'scroll')) { - if (i !== game.currentPlayerIndex && p.isAlive) { - if (game.isInRange(i)) { - playerCard.classList.add('targetable'); - } else { - playerCard.classList.add('out-of-range'); - } - } + if (hasSelectedCard && isAttackOrScroll && i !== game.currentPlayerIndex && p.isAlive) { + if (game.isInRange(i)) { + playerCard.classList.add('targetable'); + } else { + playerCard.classList.add('out-of-range'); } } @@ -1508,42 +1877,51 @@ function renderUI() { const identityEl = document.getElementById(`identity-${i}`); const identityInfo = IDENTITIES[p.identity]; - if (p.isHuman || p.identity === 'lord' || !p.isAlive) { - if (infoCache.identity !== identityInfo.name) { - identityEl.textContent = identityInfo.name; - identityEl.className = `identity ${identityInfo.class}`; - infoCache.identity = identityInfo.name; - } - } else { - if (infoCache.identity !== '???') { - identityEl.textContent = '???'; - identityEl.className = 'identity hidden'; - infoCache.identity = '???'; - } + const showIdentity = p.isHuman || p.identity === 'lord' || !p.isAlive; + const identityText = showIdentity ? identityInfo.name : '???'; + const identityClass = showIdentity ? `identity ${identityInfo.class}` : 'identity hidden'; + + if (infoCache.identity !== identityText) { + identityEl.textContent = identityText; + identityEl.className = identityClass; + infoCache.identity = identityText; } const hpFill = document.getElementById(`hp-bar-${i}`).querySelector('.hp-fill'); - hpFill.style.width = `${(p.currentHp / p.maxHp) * 100}%`; - hpFill.classList.remove('damaged'); - void hpFill.offsetWidth; - hpFill.classList.add('damaged'); - document.getElementById(`hp-value-${i}`).textContent = `${p.currentHp}/${p.maxHp}`; - document.getElementById(`cards-${i}`).textContent = p.hand.length; + + if (!hpCache[i]) { + hpCache[i] = { + currentHp: null, + maxHp: null, + handLength: null + }; + } + + const hpData = hpCache[i]; + + if (hpData.currentHp !== p.currentHp || hpData.maxHp !== p.maxHp) { + hpFill.style.width = `${(p.currentHp / p.maxHp) * 100}%`; + hpFill.classList.remove('damaged'); + void hpFill.offsetWidth; + hpFill.classList.add('damaged'); + document.getElementById(`hp-value-${i}`).textContent = `${p.currentHp}/${p.maxHp}`; + hpData.currentHp = p.currentHp; + hpData.maxHp = p.maxHp; + } + + if (hpData.handLength !== p.hand.length) { + document.getElementById(`cards-${i}`).textContent = p.hand.length; + hpData.handLength = p.hand.length; + } renderEquipment(i); const skillEl = document.getElementById(`skill-${i}`); - if (p.character.skill) { - if (infoCache.skill !== p.character.skill) { - skillEl.textContent = p.character.skill; - skillEl.title = p.character.skillDesc || ''; - infoCache.skill = p.character.skill; - } - } else { - if (infoCache.skill !== '') { - skillEl.textContent = ''; - infoCache.skill = ''; - } + const skillText = p.character.skill || ''; + if (infoCache.skill !== skillText) { + skillEl.textContent = skillText; + skillEl.title = p.character.skillDesc || ''; + infoCache.skill = skillText; } renderHand(i); @@ -1565,6 +1943,7 @@ function renderUI() { const handCache = {}; const equipmentCache = {}; const playerInfoCache = {}; +const hpCache = {}; function renderHand(playerIndex) { const player = game.players[playerIndex]; @@ -1573,41 +1952,43 @@ function renderHand(playerIndex) { if (!handCache[playerIndex]) { handCache[playerIndex] = { handLength: 0, - cards: [] + cards: [], + selectedCardIndex: -1 }; } const cache = handCache[playerIndex]; const currentHandLength = player.hand.length; + const currentSelectedIndex = player.isHuman ? game.selectedCardIndex : -1; - if (currentHandLength === cache.handLength) { - if (player.isHuman) { - const existingCards = handContainer.querySelectorAll('.card'); - player.hand.forEach((card, index) => { - const cardElement = existingCards[index]; - const cardImage = CARD_IMAGES[card.name] || { background: '#fff', icon: '🃏', pattern: 'default' }; - - const isSelected = game.selectedCardIndex === index; - const wasSelected = cardElement.classList.contains('selected'); - - if (isSelected !== wasSelected) { - cardElement.classList.toggle('selected', isSelected); - } + if (currentHandLength === cache.handLength && currentSelectedIndex === cache.selectedCardIndex) { + return; + } + + if (currentHandLength === cache.handLength && player.isHuman) { + const existingCards = handContainer.querySelectorAll('.card'); + const changedIndex = currentSelectedIndex !== cache.selectedCardIndex ? currentSelectedIndex : -1; + + if (changedIndex !== -1) { + existingCards.forEach((cardElement, index) => { + cardElement.classList.toggle('selected', index === currentSelectedIndex); }); } + cache.selectedCardIndex = currentSelectedIndex; return; } handContainer.innerHTML = ''; cache.handLength = currentHandLength; cache.cards = []; + cache.selectedCardIndex = currentSelectedIndex; if (player.isHuman) { player.hand.forEach((card, index) => { const cardElement = document.createElement('div'); const cardImage = CARD_IMAGES[card.name] || { background: '#fff', icon: '🃏', pattern: 'default' }; - cardElement.className = `card ${card.category}` + (game.selectedCardIndex === index ? ' selected' : ''); + cardElement.className = `card ${card.category}` + (index === currentSelectedIndex ? ' selected' : ''); cardElement.style.background = cardImage.background; cardElement.innerHTML = `
${cardImage.icon}
@@ -1657,46 +2038,26 @@ function renderEquipment(playerIndex) { cache.horsePlus = player.equipment.horsePlus?.name || null; cache.horseMinus = player.equipment.horseMinus?.name || null; - if (player.equipment.weapon) { - const equipCard = document.createElement('div'); - equipCard.className = 'equipment-card weapon'; - equipCard.textContent = player.equipment.weapon.name.substring(0, 2); - const effect = EQUIPMENT_EFFECTS[player.equipment.weapon.name]; - if (effect) { - equipCard.title = `${player.equipment.weapon.name}\n效果: ${effect.effect}`; + const equipmentTypes = [ + { key: 'weapon', class: 'weapon' }, + { key: 'armor', class: 'armor' }, + { key: 'horsePlus', class: 'horse' }, + { key: 'horseMinus', class: 'horse' } + ]; + + equipmentTypes.forEach(({ key, class: className }) => { + const equip = player.equipment[key]; + if (equip) { + const equipCard = document.createElement('div'); + equipCard.className = `equipment-card ${className}`; + equipCard.textContent = equip.name.substring(0, 2); + const effect = EQUIPMENT_EFFECTS[equip.name]; + if (effect) { + equipCard.title = `${equip.name}\n效果: ${effect.effect}`; + } + equipmentEl.appendChild(equipCard); } - equipmentEl.appendChild(equipCard); - } - if (player.equipment.armor) { - const equipCard = document.createElement('div'); - equipCard.className = 'equipment-card armor'; - equipCard.textContent = player.equipment.armor.name.substring(0, 2); - const effect = EQUIPMENT_EFFECTS[player.equipment.armor.name]; - if (effect) { - equipCard.title = `${player.equipment.armor.name}\n效果: ${effect.effect}`; - } - equipmentEl.appendChild(equipCard); - } - if (player.equipment.horsePlus) { - const equipCard = document.createElement('div'); - equipCard.className = 'equipment-card horse'; - equipCard.textContent = player.equipment.horsePlus.name.substring(0, 2); - const effect = EQUIPMENT_EFFECTS[player.equipment.horsePlus.name]; - if (effect) { - equipCard.title = `${player.equipment.horsePlus.name}\n效果: ${effect.effect}`; - } - equipmentEl.appendChild(equipCard); - } - if (player.equipment.horseMinus) { - const equipCard = document.createElement('div'); - equipCard.className = 'equipment-card horse'; - equipCard.textContent = player.equipment.horseMinus.name.substring(0, 2); - const effect = EQUIPMENT_EFFECTS[player.equipment.horseMinus.name]; - if (effect) { - equipCard.title = `${player.equipment.horseMinus.name}\n效果: ${effect.effect}`; - } - equipmentEl.appendChild(equipCard); - } + }); } function selectCard(index) {