优化代码结构和性能:实现基于优先级的AI出牌系统,改进UI更新缓存机制,减少重复代码
This commit is contained in:
parent
71265b4f23
commit
1e17309168
627
game.js
627
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,9 +810,107 @@ class Game {
|
||||
player.showHealEffect();
|
||||
this.addLog(`${player.character.name} 使用了【桃】,回复1点体力`);
|
||||
} else if (card.type === 'dodge') {
|
||||
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);
|
||||
this.discardCard(playedCard);
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
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 peachCard = player.hand.findIndex(c => c.type === 'peach');
|
||||
if (peachCard !== -1 && player.currentHp < player.maxHp) {
|
||||
this.playCard(peachCard);
|
||||
updateUI();
|
||||
}
|
||||
const playNextCard = () => {
|
||||
let cardsUsed = false;
|
||||
|
||||
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 (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 === '孙权' && 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 drawCard = player.hand.findIndex(c => c.type === 'draw');
|
||||
if (drawCard !== -1) {
|
||||
this.playCard(drawCard);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const equipCard = player.hand.findIndex(c => c.category === 'equip');
|
||||
if (equipCard !== -1) {
|
||||
this.playCard(equipCard);
|
||||
updateUI();
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
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,18 +1856,13 @@ 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 (hasSelectedCard && isAttackOrScroll && i !== game.currentPlayerIndex && p.isAlive) {
|
||||
if (game.isInRange(i)) {
|
||||
playerCard.classList.add('targetable');
|
||||
} else {
|
||||
playerCard.classList.add('out-of-range');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (game.selectedTargetIndex === i) {
|
||||
playerCard.classList.add('targeted');
|
||||
@ -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');
|
||||
|
||||
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;
|
||||
const skillText = p.character.skill || '';
|
||||
if (infoCache.skill !== skillText) {
|
||||
skillEl.textContent = skillText;
|
||||
skillEl.title = p.character.skillDesc || '';
|
||||
infoCache.skill = p.character.skill;
|
||||
}
|
||||
} else {
|
||||
if (infoCache.skill !== '') {
|
||||
skillEl.textContent = '';
|
||||
infoCache.skill = '';
|
||||
}
|
||||
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 = `
|
||||
<div class="card-icon">${cardImage.icon}</div>
|
||||
@ -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 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 weapon';
|
||||
equipCard.textContent = player.equipment.weapon.name.substring(0, 2);
|
||||
const effect = EQUIPMENT_EFFECTS[player.equipment.weapon.name];
|
||||
equipCard.className = `equipment-card ${className}`;
|
||||
equipCard.textContent = equip.name.substring(0, 2);
|
||||
const effect = EQUIPMENT_EFFECTS[equip.name];
|
||||
if (effect) {
|
||||
equipCard.title = `${player.equipment.weapon.name}\n效果: ${effect.effect}`;
|
||||
}
|
||||
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}`;
|
||||
equipCard.title = `${equip.name}\n效果: ${effect.effect}`;
|
||||
}
|
||||
equipmentEl.appendChild(equipCard);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function selectCard(index) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user