From 2014162819bb1df6c070daf266b4c17a431b66c6 Mon Sep 17 00:00:00 2001 From: hyper Date: Thu, 23 Apr 2026 15:28:45 +0800 Subject: [PATCH] refactor: update Regicide commands and improve code style --- src/samples/regicide/commands/index.ts | 688 ++++++++++++------------- src/samples/regicide/game.ts | 404 ++++++++------- 2 files changed, 561 insertions(+), 531 deletions(-) diff --git a/src/samples/regicide/commands/index.ts b/src/samples/regicide/commands/index.ts index dc15f7b..0b29ec7 100644 --- a/src/samples/regicide/commands/index.ts +++ b/src/samples/regicide/commands/index.ts @@ -1,421 +1,417 @@ -import {IGameContext} from "@/core/game"; -import {RegicideState} from "@/samples/regicide/state"; -import {createGameCommandRegistry} from "@/core/game"; -import {PlayerType, RegicideCard} from "@/samples/regicide/types"; -import {CARD_VALUES, FACE_CARDS} from "@/samples/regicide/constants"; -import {isEnemyDefeated} from "@/samples/regicide/utils"; +import { IGameContext } from "@/core/game"; +import { RegicideState } from "@/samples/regicide/state"; +import { PlayerType, RegicideCard } from "@/samples/regicide/types"; +import { CARD_VALUES, FACE_CARDS } from "@/samples/regicide/constants"; +import { isEnemyDefeated } from "@/samples/regicide/utils"; export type RegicideGame = IGameContext; -export const registry = createGameCommandRegistry(); - /** * 打出一张牌(对当前敌人造成伤害) */ -const playCmd = registry.register({ - schema: 'play ', - run: async (game: RegicideGame, player: string, cardId: string) => { - const state = game.value; - const card = state.cards[cardId]; +async function playCmd(game: RegicideGame, player: string, cardId: string) { + const state = game.value; + const card = state.cards[cardId]; - if (!card) { - return {success: false, error: `卡牌 ${cardId} 不存在`}; - } + if (!card) { + return { success: false, error: `卡牌 ${cardId} 不存在` }; + } - // 检查卡牌是否在玩家手牌中 - const playerKey = player as PlayerType; - const playerHand = state.playerHands[playerKey]; - if (!playerHand || !playerHand.includes(cardId)) { - return {success: false, error: `卡牌 ${cardId} 不在玩家 ${player} 的手牌中`}; - } + // 检查卡牌是否在玩家手牌中 + const playerKey = player as PlayerType; + const playerHand = state.playerHands[playerKey]; + if (!playerHand || !playerHand.includes(cardId)) { + return { + success: false, + error: `卡牌 ${cardId} 不在玩家 ${player} 的手牌中`, + }; + } - // 检查是否有当前敌人 - if (!state.currentEnemy) { - return {success: false, error: '没有活跃的敌人'}; - } + // 检查是否有当前敌人 + if (!state.currentEnemy) { + return { success: false, error: "没有活跃的敌人" }; + } - // 计算伤害(基础伤害为卡牌面值) - let damage = card.value; - let attackReduction = 0; + // 计算伤害(基础伤害为卡牌面值) + let damage = card.value; + let attackReduction = 0; - // 梅花双倍伤害 - if (card.suit === 'clubs') { - damage *= 2; - } + // 梅花双倍伤害 + if (card.suit === "clubs") { + damage *= 2; + } - // 黑桃降低敌人攻击力 - if (card.suit === 'spades') { - attackReduction = card.value; - } + // 黑桃降低敌人攻击力 + if (card.suit === "spades") { + attackReduction = card.value; + } - const enemyHpBefore = state.currentEnemy.hp; + const enemyHpBefore = state.currentEnemy.hp; - await game.produce(state => { - // 对敌人造成伤害 - state.currentEnemy!.hp -= damage; + await game.produceAsync((state) => { + // 对敌人造成伤害 + state.currentEnemy!.hp -= damage; - // 记录黑桃的攻击力降低 - if (attackReduction > 0) { - state.currentEnemy!.value = Math.max(0, state.currentEnemy!.value - attackReduction); - } - - // 从手牌移除卡牌 - const hand = state.playerHands[playerKey]; - const cardIndex = hand.indexOf(cardId); - if (cardIndex !== -1) { - hand.splice(cardIndex, 1); - } - - // 将卡牌移到弃牌堆 - state.cards[cardId].regionId = 'discardPile'; - - // 红心能力:将弃牌堆洗回酒馆牌堆 - if (card.suit === 'hearts') { - const discardIds = state.regions.discardPile.childIds.filter(id => id !== state.currentEnemy!.id); - if (discardIds.length > 0) { - // 将弃牌堆(除当前敌人外)移回酒馆牌堆 - for (const discardId of discardIds) { - state.cards[discardId].regionId = 'tavernDeck'; - } - state.regions.tavernDeck.childIds.push(...discardIds); - state.regions.discardPile.childIds = [state.currentEnemy!.id]; - } - } - - // 方块能力:从酒馆牌堆抓牌 - if (card.suit === 'diamonds') { - const tavernDeckCount = state.regions.tavernDeck.childIds.length; - if (tavernDeckCount > 0) { - const drawCardId = state.regions.tavernDeck.childIds.shift()!; - state.cards[drawCardId].regionId = `hand_${player}`; - hand.push(drawCardId); - } - } - }); - - // 检查敌人是否被击败 - const enemyDefeated = isEnemyDefeated(game.value.currentEnemy); - - return { - success: true, - result: { - damage, - attackReduction, - enemyHpBefore, - enemyHpAfter: game.value.currentEnemy!.hp, - enemyDefeated, - suitAbility: card.suit - } - }; + // 记录黑桃的攻击力降低 + if (attackReduction > 0) { + state.currentEnemy!.value = Math.max( + 0, + state.currentEnemy!.value - attackReduction, + ); } -}); + + // 从手牌移除卡牌 + const hand = state.playerHands[playerKey]; + const cardIndex = hand.indexOf(cardId); + if (cardIndex !== -1) { + hand.splice(cardIndex, 1); + } + + // 将卡牌移到弃牌堆 + state.cards[cardId].regionId = "discardPile"; + + // 红心能力:将弃牌堆洗回酒馆牌堆 + if (card.suit === "hearts") { + const discardIds = state.regions.discardPile.childIds.filter( + (id) => id !== state.currentEnemy!.id, + ); + if (discardIds.length > 0) { + // 将弃牌堆(除当前敌人外)移回酒馆牌堆 + for (const discardId of discardIds) { + state.cards[discardId].regionId = "tavernDeck"; + } + state.regions.tavernDeck.childIds.push(...discardIds); + state.regions.discardPile.childIds = [state.currentEnemy!.id]; + } + } + + // 方块能力:从酒馆牌堆抓牌 + if (card.suit === "diamonds") { + const tavernDeckCount = state.regions.tavernDeck.childIds.length; + if (tavernDeckCount > 0) { + const drawCardId = state.regions.tavernDeck.childIds.shift()!; + state.cards[drawCardId].regionId = `hand_${player}`; + hand.push(drawCardId); + } + } + }); + + // 检查敌人是否被击败 + const enemyDefeated = isEnemyDefeated(game.value.currentEnemy); + + return { + success: true, + result: { + damage, + attackReduction, + enemyHpBefore, + enemyHpAfter: game.value.currentEnemy!.hp, + enemyDefeated, + suitAbility: card.suit, + }, + }; +} /** * 打出A配合另一张牌 */ -const playWithACmd = registry.register({ - schema: 'play-with-a ', - run: async (game: RegicideGame, player: string, aceCardId: string, otherCardId: string) => { - const state = game.value; - const aceCard = state.cards[aceCardId]; - const otherCard = state.cards[otherCardId]; +async function playWithACmd( + game: RegicideGame, + player: string, + aceCardId: string, + otherCardId: string, +) { + const state = game.value; + const aceCard = state.cards[aceCardId]; + const otherCard = state.cards[otherCardId]; - if (!aceCard || !otherCard) { - return {success: false, error: '卡牌不存在'}; - } + if (!aceCard || !otherCard) { + return { success: false, error: "卡牌不存在" }; + } - // 检查是否是A牌 - if (aceCard.rank !== 'A') { - return {success: false, error: `第一张牌必须是A`}; - } + // 检查是否是A牌 + if (aceCard.rank !== "A") { + return { success: false, error: `第一张牌必须是A` }; + } - const playerKey = player as PlayerType; - const playerHand = state.playerHands[playerKey]; + const playerKey = player as PlayerType; + const playerHand = state.playerHands[playerKey]; - // 检查两张牌都在手牌中 - if (!playerHand.includes(aceCardId) || !playerHand.includes(otherCardId)) { - return {success: false, error: '卡牌不在手牌中'}; - } + // 检查两张牌都在手牌中 + if (!playerHand.includes(aceCardId) || !playerHand.includes(otherCardId)) { + return { success: false, error: "卡牌不在手牌中" }; + } - if (!state.currentEnemy) { - return {success: false, error: '没有活跃的敌人'}; - } + if (!state.currentEnemy) { + return { success: false, error: "没有活跃的敌人" }; + } - // 计算两张牌的总伤害 - let totalDamage = aceCard.value + otherCard.value; + // 计算两张牌的总伤害 + let totalDamage = aceCard.value + otherCard.value; - // 如果另一张牌是梅花,双倍伤害 - if (otherCard.suit === 'clubs') { - totalDamage *= 2; - } + // 如果另一张牌是梅花,双倍伤害 + if (otherCard.suit === "clubs") { + totalDamage *= 2; + } - let attackReduction = 0; - if (aceCard.suit === 'spades') { - attackReduction += aceCard.value; - } - if (otherCard.suit === 'spades') { - attackReduction += otherCard.value; - } + let attackReduction = 0; + if (aceCard.suit === "spades") { + attackReduction += aceCard.value; + } + if (otherCard.suit === "spades") { + attackReduction += otherCard.value; + } - await game.produce(state => { - // 对敌人造成伤害 - state.currentEnemy!.hp -= totalDamage; + await game.produceAsync((state) => { + // 对敌人造成伤害 + state.currentEnemy!.hp -= totalDamage; - // 记录黑桃的攻击力降低 - if (attackReduction > 0) { - state.currentEnemy!.value = Math.max(0, state.currentEnemy!.value - attackReduction); - } - - // 从手牌移除两张牌 - const hand = state.playerHands[playerKey]; - const aceIndex = hand.indexOf(aceCardId); - const otherIndex = hand.indexOf(otherCardId); - if (aceIndex !== -1) hand.splice(aceIndex, 1); - if (otherIndex !== -1) hand.splice(otherIndex, 1); - - // 将卡牌移到弃牌堆 - state.cards[aceCardId].regionId = 'discardPile'; - state.cards[otherCardId].regionId = 'discardPile'; - }); - - const enemyDefeated = isEnemyDefeated(state.currentEnemy); - - return { - success: true, - result: { - damage: totalDamage, - attackReduction, - enemyHp: state.currentEnemy!.hp, - enemyDefeated - } - }; + // 记录黑桃的攻击力降低 + if (attackReduction > 0) { + state.currentEnemy!.value = Math.max( + 0, + state.currentEnemy!.value - attackReduction, + ); } -}); + + // 从手牌移除两张牌 + const hand = state.playerHands[playerKey]; + const aceIndex = hand.indexOf(aceCardId); + const otherIndex = hand.indexOf(otherCardId); + if (aceIndex !== -1) hand.splice(aceIndex, 1); + if (otherIndex !== -1) hand.splice(otherIndex, 1); + + // 将卡牌移到弃牌堆 + state.cards[aceCardId].regionId = "discardPile"; + state.cards[otherCardId].regionId = "discardPile"; + }); + + const enemyDefeated = isEnemyDefeated(state.currentEnemy); + + return { + success: true, + result: { + damage: totalDamage, + attackReduction, + enemyHp: state.currentEnemy!.hp, + enemyDefeated, + }, + }; +} /** * 让过(不出牌) */ -const passCmd = registry.register({ - schema: 'pass ', - run: async (game: RegicideGame, player: string) => { - // 即使让过,也会受到敌人反击(在回合结束时处理) - return {success: true, result: {message: `${player} 让过`}}; - } -}); +async function passCmd(game: RegicideGame, player: string) { + // 即使让过,也会受到敌人反击(在回合结束时处理) + return { success: true, result: { message: `${player} 让过` } }; +} /** * 敌人反击 - 玩家必须弃掉点数和 >= 敌人攻击力的牌 */ -const enemyCounterattackCmd = registry.register({ - schema: 'counterattack ', - run: async (game: RegicideGame, player: string, discardCards: string[]) => { - const state = game.value; +async function enemyCounterattackCmd( + game: RegicideGame, + player: string, + discardCards: string[], +) { + const state = game.value; - if (!state.currentEnemy) { - return {success: false, error: '没有活跃的敌人'}; - } + if (!state.currentEnemy) { + return { success: false, error: "没有活跃的敌人" }; + } - const playerKey = player as PlayerType; - const playerHand = state.playerHands[playerKey]; + const playerKey = player as PlayerType; + const playerHand = state.playerHands[playerKey]; - // 检查要弃的牌都在手牌中 - for (const cardId of discardCards) { - if (!playerHand.includes(cardId)) { - return {success: false, error: `卡牌 ${cardId} 不在手牌中`}; - } - } - - // 计算弃牌的点数和 - let totalValue = 0; - for (const cardId of discardCards) { - const card = state.cards[cardId]; - if (card) { - totalValue += card.value; - } - } - - const enemyAttack = state.currentEnemy.value; - - // 检查点数和是否 >= 敌人攻击力 - if (totalValue < enemyAttack) { - return { - success: false, - error: `弃牌点数和 (${totalValue}) 小于敌人攻击力 (${enemyAttack}),游戏失败` - }; - } - - // 执行弃牌 - await game.produce(state => { - const hand = state.playerHands[playerKey]; - for (const cardId of discardCards) { - const index = hand.indexOf(cardId); - if (index !== -1) { - hand.splice(index, 1); - } - state.cards[cardId].regionId = 'discardPile'; - } - }); - - return { - success: true, - result: { - discardedCards: discardCards, - totalValue, - enemyAttack - } - }; + // 检查要弃的牌都在手牌中 + for (const cardId of discardCards) { + if (!playerHand.includes(cardId)) { + return { success: false, error: `卡牌 ${cardId} 不在手牌中` }; } -}); + } + + // 计算弃牌的点数和 + let totalValue = 0; + for (const cardId of discardCards) { + const card = state.cards[cardId]; + if (card) { + totalValue += card.value; + } + } + + const enemyAttack = state.currentEnemy.value; + + // 检查点数和是否 >= 敌人攻击力 + if (totalValue < enemyAttack) { + return { + success: false, + error: `弃牌点数和 (${totalValue}) 小于敌人攻击力 (${enemyAttack}),游戏失败`, + }; + } + + // 执行弃牌 + await game.produceAsync((state) => { + const hand = state.playerHands[playerKey]; + for (const cardId of discardCards) { + const index = hand.indexOf(cardId); + if (index !== -1) { + hand.splice(index, 1); + } + state.cards[cardId].regionId = "discardPile"; + } + }); + + return { + success: true, + result: { + discardedCards: discardCards, + totalValue, + enemyAttack, + }, + }; +} /** * 检查敌人是否被击败,如果击败则翻开下一个敌人 */ -const checkEnemyDefeatedCmd = registry.register({ - schema: 'check-enemy', - run: async (game: RegicideGame) => { - const state = game.value; +async function checkEnemyDefeatedCmd(game: RegicideGame) { + const state = game.value; - if (!state.currentEnemy) { - return {success: false, error: '没有活跃的敌人'}; - } + if (!state.currentEnemy) { + return { success: false as const, error: "没有活跃的敌人" }; + } - const defeated = state.currentEnemy.hp <= 0; + const defeated = state.currentEnemy.hp <= 0; - if (defeated) { - const defeatedEnemy = {...state.currentEnemy}; + if (defeated) { + const defeatedEnemy = { ...state.currentEnemy }; - await game.produce(state => { - // 将当前敌人移到弃牌堆 - state.regions.discardPile.childIds.push(state.currentEnemy!.id); + await game.produceAsync((state) => { + // 将当前敌人移到弃牌堆 + state.regions.discardPile.childIds.push(state.currentEnemy!.id); - // 翻开下一个敌人 - if (state.enemyDeck.length > 0) { - const nextEnemy = state.enemyDeck.shift()!; - state.currentEnemy = nextEnemy; - } else { - // 没有更多敌人了 - state.currentEnemy = null; - } - }); + // 翻开下一个敌人 + if (state.enemyDeck.length > 0) { + const nextEnemy = state.enemyDeck.shift()!; + state.currentEnemy = nextEnemy; + } else { + // 没有更多敌人了 + state.currentEnemy = null; + } + }); - // 检查是否胜利(没有更多敌人) - if (!game.value.currentEnemy) { - await game.produce(state => { - state.phase = 'victory'; - state.winner = true; - }); - } - - return { - success: true, - result: { - defeated: true, - defeatedEnemy, - nextEnemy: game.value.currentEnemy, - enemiesRemaining: game.value.enemyDeck.length - } - }; - } - - return { - success: true, - result: { - defeated: false, - currentEnemy: {...state.currentEnemy}, - enemiesRemaining: state.enemyDeck.length - } - }; + // 检查是否胜利(没有更多敌人) + if (!game.value.currentEnemy) { + await game.produceAsync((state) => { + state.phase = "victory"; + state.winner = true; + }); } -}); + + return { + success: true as const, + result: { + defeated: true, + defeatedEnemy, + nextEnemy: game.value.currentEnemy, + enemiesRemaining: game.value.enemyDeck.length, + }, + }; + } + + return { + success: true, + result: { + defeated: false, + currentEnemy: { ...state.currentEnemy }, + enemiesRemaining: state.enemyDeck.length, + }, + }; +} /** * 检查玩家是否有可出的牌 */ -const checkCanPlayCmd = registry.register({ - schema: 'check-can-play ', - run: async (game: RegicideGame, player: string) => { - const state = game.value; - const playerKey = player as PlayerType; - const playerHand = state.playerHands[playerKey]; +async function checkCanPlayCmd(game: RegicideGame, player: string) { + const state = game.value; + const playerKey = player as PlayerType; + const playerHand = state.playerHands[playerKey]; - const canPlay = playerHand.length > 0; - const canPlayWithA = playerHand.some(cardId => { - const card = state.cards[cardId]; - return card && card.rank === 'A' && playerHand.length > 1; - }); + const canPlay = playerHand.length > 0; + const canPlayWithA = playerHand.some((cardId) => { + const card = state.cards[cardId]; + return card && card.rank === "A" && playerHand.length > 1; + }); - return { - success: true, - result: { - canPlay, - canPlayWithA, - handSize: playerHand.length - } - }; - } -}); + return { + success: true, + result: { + canPlay, + canPlayWithA, + handSize: playerHand.length, + }, + }; +} /** * 检查酒馆牌堆是否为空 */ -const checkTavernDeckCmd = registry.register({ - schema: 'check-tavern-deck', - run: async (game: RegicideGame) => { - const state = game.value; - const isEmpty = state.regions.tavernDeck.childIds.length === 0; +async function checkTavernDeckCmd(game: RegicideGame) { + const state = game.value; + const isEmpty = state.regions.tavernDeck.childIds.length === 0; - // 如果酒馆牌堆为空且所有玩家手牌也为空,则游戏失败 - if (isEmpty) { - const allHandsEmpty = Object.values(state.playerHands).every(hand => hand.length === 0); - if (allHandsEmpty) { - await game.produce(state => { - state.phase = 'defeat'; - state.winner = false; - }); - } - } - - return { - success: true, - result: { - isEmpty, - cardsRemaining: state.regions.tavernDeck.childIds.length - } - }; + // 如果酒馆牌堆为空且所有玩家手牌也为空,则游戏失败 + if (isEmpty) { + const allHandsEmpty = Object.values(state.playerHands).every( + (hand) => hand.length === 0, + ); + if (allHandsEmpty) { + await game.produceAsync((state) => { + state.phase = "defeat"; + state.winner = false; + }); } -}); + } + + return { + success: true, + result: { + isEmpty, + cardsRemaining: state.regions.tavernDeck.childIds.length, + }, + }; +} /** * 下一个玩家回合 */ -const nextTurnCmd = registry.register({ - schema: 'next-turn', - run: async (game: RegicideGame) => { - const state = game.value; - await game.produce(state => { - state.currentPlayerIndex = (state.currentPlayerIndex + 1) % state.playerCount; - }); +async function nextTurnCmd(game: RegicideGame) { + const state = game.value; + await game.produce((state) => { + state.currentPlayerIndex = + (state.currentPlayerIndex + 1) % state.playerCount; + }); - const players: PlayerType[] = ['player1', 'player2', 'player3', 'player4']; - const currentPlayer = players[game.value.currentPlayerIndex]; + const players: PlayerType[] = ["player1", "player2", "player3", "player4"]; + const currentPlayer = players[game.value.currentPlayerIndex]; - return { - success: true, - result: { - currentPlayer, - currentPlayerIndex: game.value.currentPlayerIndex - } - }; - } -}); + return { + success: true, + result: { + currentPlayer, + currentPlayerIndex: game.value.currentPlayerIndex, + }, + }; +} export { - playCmd as play, - playWithACmd as playWithA, - passCmd as pass, - enemyCounterattackCmd as enemyCounterattack, - checkEnemyDefeatedCmd as checkEnemy, - checkCanPlayCmd as checkCanPlay, - checkTavernDeckCmd as checkTavernDeck, - nextTurnCmd as nextTurn, + playCmd as play, + playWithACmd as playWithA, + passCmd as pass, + enemyCounterattackCmd as enemyCounterattack, + checkEnemyDefeatedCmd as checkEnemy, + checkCanPlayCmd as checkCanPlay, + checkTavernDeckCmd as checkTavernDeck, + nextTurnCmd as nextTurn, }; diff --git a/src/samples/regicide/game.ts b/src/samples/regicide/game.ts index e2b7c89..971c433 100644 --- a/src/samples/regicide/game.ts +++ b/src/samples/regicide/game.ts @@ -1,8 +1,15 @@ -import {IGameContext} from "@/core/game"; -import {RegicideState} from "@/samples/regicide/state"; -import {buildEnemyDeck, buildTavernDeck, createAllCards, getPlayerHandRegionId} from "@/samples/regicide/utils"; -import {INITIAL_HAND_SIZE} from "@/samples/regicide/constants"; -import {Enemy, PlayerType, RegicideCard} from "@/samples/regicide/types"; +import { IGameContext } from "@/core/game"; +import { RegicideState } from "@/samples/regicide/state"; +import { + buildEnemyDeck, + buildTavernDeck, + createAllCards, + getPlayerHandRegionId, +} from "@/samples/regicide/utils"; +import { INITIAL_HAND_SIZE } from "@/samples/regicide/constants"; +import { Enemy, PlayerType, RegicideCard } from "@/samples/regicide/types"; +import { checkEnemy, nextTurn, pass, playWithA } from "./commands"; +import { enemyCounterattackCmd, playCmd } from "."; export type RegicideGame = IGameContext; @@ -12,228 +19,255 @@ export type RegicideGame = IGameContext; * @param playerCount 玩家数量(1-4) * @param seed 随机种子(可选) */ -export async function setupGame(game: RegicideGame, playerCount: number, seed?: number) { - if (playerCount < 1 || playerCount > 4) { - throw new Error('玩家数量必须为 1-4 人'); +export async function setupGame( + game: RegicideGame, + playerCount: number, + seed?: number, +) { + if (playerCount < 1 || playerCount > 4) { + throw new Error("玩家数量必须为 1-4 人"); + } + + if (seed) { + // RNG seeding handled by game context + } + + // 创建所有卡牌 + const allCards = createAllCards(); + + // 构建敌人牌堆(J/Q/K) + const enemyDeck = buildEnemyDeck(game._rng); + + // 构建酒馆牌堆(A-10) + const tavernDeck = buildTavernDeck(game._rng); + + // 初始化游戏状态 + await game.produceAsync((state) => { + state.cards = allCards; + state.playerCount = playerCount; + state.currentPlayerIndex = 0; + state.enemyDeck = enemyDeck; + + // 设置酒馆牌堆区域 + for (const card of tavernDeck) { + card.regionId = "tavernDeck"; + state.regions.tavernDeck.childIds.push(card.id); } - if (seed) { - // RNG seeding handled by game context + // 设置敌人牌堆区域(只存储ID,敌人是独立对象) + state.regions.enemyDeck.childIds = enemyDeck.map((e) => e.id); + + // 给每个玩家发牌 + const players: PlayerType[] = ["player1", "player2", "player3", "player4"]; + for (let i = 0; i < playerCount; i++) { + const player = players[i]; + const regionId = getPlayerHandRegionId(player); + + for (let j = 0; j < INITIAL_HAND_SIZE; j++) { + if (tavernDeck.length === 0) break; + const card = tavernDeck.shift()!; + card.regionId = regionId; + state.playerHands[player].push(card.id); + const region = state.regions[regionId as keyof typeof state.regions]; + region.childIds.push(card.id); + } } - // 创建所有卡牌 - const allCards = createAllCards(); - - // 构建敌人牌堆(J/Q/K) - const enemyDeck = buildEnemyDeck(game._rng); - - // 构建酒馆牌堆(A-10) - const tavernDeck = buildTavernDeck(game._rng); - - // 初始化游戏状态 - await game.produceAsync(state => { - state.cards = allCards; - state.playerCount = playerCount; - state.currentPlayerIndex = 0; - state.enemyDeck = enemyDeck; - - // 设置酒馆牌堆区域 - for (const card of tavernDeck) { - card.regionId = 'tavernDeck'; - state.regions.tavernDeck.childIds.push(card.id); - } - - // 设置敌人牌堆区域(只存储ID,敌人是独立对象) - state.regions.enemyDeck.childIds = enemyDeck.map(e => e.id); - - // 给每个玩家发牌 - const players: PlayerType[] = ['player1', 'player2', 'player3', 'player4']; - for (let i = 0; i < playerCount; i++) { - const player = players[i]; - const regionId = getPlayerHandRegionId(player); - - for (let j = 0; j < INITIAL_HAND_SIZE; j++) { - if (tavernDeck.length === 0) break; - const card = tavernDeck.shift()!; - card.regionId = regionId; - state.playerHands[player].push(card.id); - const region = state.regions[regionId as keyof typeof state.regions]; - region.childIds.push(card.id); - } - } - - // 翻开第一个敌人 - if (enemyDeck.length > 0) { - const firstEnemy = enemyDeck.shift()!; - state.currentEnemy = firstEnemy; - } - }); + // 翻开第一个敌人 + if (enemyDeck.length > 0) { + const firstEnemy = enemyDeck.shift()!; + state.currentEnemy = firstEnemy; + } + }); } /** * 启动游戏主循环 */ export async function start(game: RegicideGame) { - const state = game.value; + const state = game.value; - // 检查游戏是否已设置 - if (!state.currentEnemy) { - throw new Error('请先调用 setupGame 初始化游戏'); + // 检查游戏是否已设置 + if (!state.currentEnemy) { + throw new Error("请先调用 setupGame 初始化游戏"); + } + + const players: PlayerType[] = ["player1", "player2", "player3", "player4"]; + + // 主游戏循环 + while (state.phase === "playing") { + const currentPlayerIndex = state.currentPlayerIndex; + const currentPlayer = players[currentPlayerIndex]; + + // 检查当前玩家是否有手牌 + const currentHand = state.playerHands[currentPlayer]; + if (currentHand.length === 0) { + // 玩家没有手牌,跳过回合 + await game.produceAsync((state) => { + state.currentPlayerIndex = + (state.currentPlayerIndex + 1) % state.playerCount; + }); + continue; } - const players: PlayerType[] = ['player1', 'player2', 'player3', 'player4']; + // 等待玩家输入(出牌或让过) + // 这里需要外部通过 prompt 系统获取输入 + // 实际使用时由 UI 或测试代码提供输入 - // 主游戏循环 - while (state.phase === 'playing') { - const currentPlayerIndex = state.currentPlayerIndex; - const currentPlayer = players[currentPlayerIndex]; + // 循环会在外部调用 play/pass 命令后继续 + // 当 phase 变为 'victory' 或 'defeat' 时退出 + break; + } - // 检查当前玩家是否有手牌 - const currentHand = state.playerHands[currentPlayer]; - if (currentHand.length === 0) { - // 玩家没有手牌,跳过回合 - await game.produceAsync(state => { - state.currentPlayerIndex = (state.currentPlayerIndex + 1) % state.playerCount; - }); - continue; - } - - // 等待玩家输入(出牌或让过) - // 这里需要外部通过 prompt 系统获取输入 - // 实际使用时由 UI 或测试代码提供输入 - - // 循环会在外部调用 play/pass 命令后继续 - // 当 phase 变为 'victory' 或 'defeat' 时退出 - break; - } - - return game.value; + return game.value; } /** * 处理完整的玩家回合 */ -export async function playTurn(game: RegicideGame, player: PlayerType, action: 'play' | 'pass', cardId?: string, secondCardId?: string) { - const state = game.value; +export async function playTurn( + game: RegicideGame, + player: PlayerType, + action: "play" | "pass", + cardId?: string, + secondCardId?: string, +) { + const state = game.value; - if (state.phase !== 'playing') { - return {success: false, error: '游戏已结束'}; - } + if (state.phase !== "playing") { + return { success: false, error: "游戏已结束" }; + } - if (!state.currentEnemy) { - return {success: false, error: '没有活跃的敌人'}; - } + if (!state.currentEnemy) { + return { success: false, error: "没有活跃的敌人" }; + } - let playResult: any; + let playResult: any; - // 执行玩家动作 - if (action === 'play' && cardId) { - // 检查是否是A配合另一张牌 - const card = state.cards[cardId]; - if (card.rank === 'A' && secondCardId) { - playResult = await game.run(`play-with-a ${player} ${cardId} ${secondCardId}`); - } else { - playResult = await game.run(`play ${player} ${cardId}`); - } + // 执行玩家动作 + if (action === "play" && cardId) { + // 检查是否是A配合另一张牌 + const card = state.cards[cardId]; + if (card.rank === "A" && secondCardId) { + playResult = await playWithA(game, player, cardId, secondCardId); } else { - // 让过 - playResult = await game.run(`pass ${player}`); + playResult = await playCmd(game, player, cardId); } + } else { + // 让过 + playResult = await pass(game, player); + } - if (!playResult.success) { - return playResult; - } + if (!playResult.success) { + return playResult; + } - // 检查敌人是否被击败 - const checkResult = await game.run<{defeated: boolean; currentEnemy?: any; nextEnemy?: any; defeatedEnemy?: any; enemiesRemaining?: number}>('check-enemy'); - if (!checkResult.success) { - return checkResult; - } - - // 如果敌人未被击败,处理反击 - if (!checkResult.result.defeated) { - // 反击逻辑需要玩家选择弃牌,这里返回状态让外部处理 - return { - success: true, - result: { - playResult: playResult.result, - enemyDefeated: false, - needsDiscard: true, - enemyAttack: state.currentEnemy.value, - playerHand: state.playerHands[player] - } - }; - } - - // 敌人被击败,检查是否还有更多敌人 - if (state.enemyDeck.length === 0 && state.currentEnemy && state.currentEnemy.hp <= 0) { - await game.produceAsync(state => { - state.phase = 'victory'; - state.winner = true; - }); - return { - success: true, - result: { - playResult: playResult.result, - enemyDefeated: true, - gameWon: true - } - }; - } - - // 切换到下一个玩家 - await game.run('next-turn'); + // 检查敌人是否被击败 + const checkResult = await checkEnemy(game); + if (!checkResult.success) { + return checkResult; + } + // 如果敌人未被击败,处理反击 + if (!checkResult.result.defeated) { + // 反击逻辑需要玩家选择弃牌,这里返回状态让外部处理 return { - success: true, - result: { - playResult: playResult.result, - enemyDefeated: true, - nextEnemy: state.currentEnemy - } + success: true, + result: { + playResult: playResult.result, + enemyDefeated: false, + needsDiscard: true, + enemyAttack: state.currentEnemy.value, + playerHand: state.playerHands[player], + }, }; + } + + // 敌人被击败,检查是否还有更多敌人 + if ( + state.enemyDeck.length === 0 && + state.currentEnemy && + state.currentEnemy.hp <= 0 + ) { + await game.produceAsync((state) => { + state.phase = "victory"; + state.winner = true; + }); + return { + success: true, + result: { + playResult: playResult.result, + enemyDefeated: true, + gameWon: true, + }, + }; + } + + // 切换到下一个玩家 + await nextTurn(game); + + return { + success: true, + result: { + playResult: playResult.result, + enemyDefeated: true, + nextEnemy: state.currentEnemy, + }, + }; } /** * 处理反击阶段的弃牌 */ -export async function handleCounterattack(game: RegicideGame, player: PlayerType, discardCardIds: string[]) { - const result = await game.run(`counterattack ${player} ${JSON.stringify(discardCardIds)}`); - - if (!result.success) { - // 弃牌失败(点数和不足),游戏失败 - await game.produceAsync(state => { - state.phase = 'defeat'; - state.winner = false; - }); - return result; - } - - // 弃牌成功,切换到下一个玩家 - await game.run('next-turn'); - +export async function handleCounterattack( + game: RegicideGame, + player: PlayerType, + discardCardIds: string[], +) { + const result = await enemyCounterattackCmd(game, player, discardCardIds); + if (!result.success) { + // 弃牌失败(点数和不足),游戏失败 + await game.produceAsync((state) => { + state.phase = "defeat"; + state.winner = false; + }); return result; + } + + // 弃牌成功,切换到下一个玩家 + await nextTurn(game); + + return result; } /** * 获取当前游戏状态摘要 */ export function getGameStatus(game: RegicideGame) { - const state = game.value; + const state = game.value; - return { - phase: state.phase, - currentPlayer: ['player1', 'player2', 'player3', 'player4'][state.currentPlayerIndex], - currentEnemy: state.currentEnemy ? { - ...state.currentEnemy, - hpPercent: Math.round((state.currentEnemy.hp / state.currentEnemy.maxHp) * 100) - } : null, - enemiesRemaining: state.enemyDeck.length, - tavernDeckCount: state.regions.tavernDeck.childIds.length, - discardPileCount: state.regions.discardPile.childIds.length, - playerHands: Object.fromEntries( - Object.entries(state.playerHands).map(([player, hand]) => [player, hand.length]) - ), - winner: state.winner - }; + return { + phase: state.phase, + currentPlayer: ["player1", "player2", "player3", "player4"][ + state.currentPlayerIndex + ], + currentEnemy: state.currentEnemy + ? { + ...state.currentEnemy, + hpPercent: Math.round( + (state.currentEnemy.hp / state.currentEnemy.maxHp) * 100, + ), + } + : null, + enemiesRemaining: state.enemyDeck.length, + tavernDeckCount: state.regions.tavernDeck.childIds.length, + discardPileCount: state.regions.discardPile.childIds.length, + playerHands: Object.fromEntries( + Object.entries(state.playerHands).map(([player, hand]) => [ + player, + hand.length, + ]), + ), + winner: state.winner, + }; }