refactor: update Regicide commands and improve code style

This commit is contained in:
hyper 2026-04-23 15:28:45 +08:00
parent d932c2f5e2
commit 2014162819
2 changed files with 561 additions and 531 deletions

View File

@ -1,421 +1,417 @@
import {IGameContext} from "@/core/game"; import { IGameContext } from "@/core/game";
import {RegicideState} from "@/samples/regicide/state"; import { RegicideState } from "@/samples/regicide/state";
import {createGameCommandRegistry} from "@/core/game"; import { PlayerType, RegicideCard } from "@/samples/regicide/types";
import {PlayerType, RegicideCard} from "@/samples/regicide/types"; import { CARD_VALUES, FACE_CARDS } from "@/samples/regicide/constants";
import {CARD_VALUES, FACE_CARDS} from "@/samples/regicide/constants"; import { isEnemyDefeated } from "@/samples/regicide/utils";
import {isEnemyDefeated} from "@/samples/regicide/utils";
export type RegicideGame = IGameContext<RegicideState>; export type RegicideGame = IGameContext<RegicideState>;
export const registry = createGameCommandRegistry<RegicideState>();
/** /**
* *
*/ */
const playCmd = registry.register({ async function playCmd(game: RegicideGame, player: string, cardId: string) {
schema: 'play <player:string> <cardId:string>', const state = game.value;
run: async (game: RegicideGame, player: string, cardId: string) => { const card = state.cards[cardId];
const state = game.value;
const card = state.cards[cardId];
if (!card) { if (!card) {
return {success: false, error: `卡牌 ${cardId} 不存在`}; return { success: false, error: `卡牌 ${cardId} 不存在` };
} }
// 检查卡牌是否在玩家手牌中 // 检查卡牌是否在玩家手牌中
const playerKey = player as PlayerType; const playerKey = player as PlayerType;
const playerHand = state.playerHands[playerKey]; const playerHand = state.playerHands[playerKey];
if (!playerHand || !playerHand.includes(cardId)) { if (!playerHand || !playerHand.includes(cardId)) {
return {success: false, error: `卡牌 ${cardId} 不在玩家 ${player} 的手牌中`}; return {
} success: false,
error: `卡牌 ${cardId} 不在玩家 ${player} 的手牌中`,
};
}
// 检查是否有当前敌人 // 检查是否有当前敌人
if (!state.currentEnemy) { if (!state.currentEnemy) {
return {success: false, error: '没有活跃的敌人'}; return { success: false, error: "没有活跃的敌人" };
} }
// 计算伤害(基础伤害为卡牌面值) // 计算伤害(基础伤害为卡牌面值)
let damage = card.value; let damage = card.value;
let attackReduction = 0; let attackReduction = 0;
// 梅花双倍伤害 // 梅花双倍伤害
if (card.suit === 'clubs') { if (card.suit === "clubs") {
damage *= 2; damage *= 2;
} }
// 黑桃降低敌人攻击力 // 黑桃降低敌人攻击力
if (card.suit === 'spades') { if (card.suit === "spades") {
attackReduction = card.value; attackReduction = card.value;
} }
const enemyHpBefore = state.currentEnemy.hp; const enemyHpBefore = state.currentEnemy.hp;
await game.produce(state => { await game.produceAsync((state) => {
// 对敌人造成伤害 // 对敌人造成伤害
state.currentEnemy!.hp -= damage; state.currentEnemy!.hp -= damage;
// 记录黑桃的攻击力降低 // 记录黑桃的攻击力降低
if (attackReduction > 0) { if (attackReduction > 0) {
state.currentEnemy!.value = Math.max(0, state.currentEnemy!.value - attackReduction); 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
}
};
} }
});
// 从手牌移除卡牌
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配合另一张牌 * A配合另一张牌
*/ */
const playWithACmd = registry.register({ async function playWithACmd(
schema: 'play-with-a <player:string> <aceCardId:string> <otherCardId:string>', game: RegicideGame,
run: async (game: RegicideGame, player: string, aceCardId: string, otherCardId: string) => { player: string,
const state = game.value; aceCardId: string,
const aceCard = state.cards[aceCardId]; otherCardId: string,
const otherCard = state.cards[otherCardId]; ) {
const state = game.value;
const aceCard = state.cards[aceCardId];
const otherCard = state.cards[otherCardId];
if (!aceCard || !otherCard) { if (!aceCard || !otherCard) {
return {success: false, error: '卡牌不存在'}; return { success: false, error: "卡牌不存在" };
} }
// 检查是否是A牌 // 检查是否是A牌
if (aceCard.rank !== 'A') { if (aceCard.rank !== "A") {
return {success: false, error: `第一张牌必须是A`}; return { success: false, error: `第一张牌必须是A` };
} }
const playerKey = player as PlayerType; const playerKey = player as PlayerType;
const playerHand = state.playerHands[playerKey]; const playerHand = state.playerHands[playerKey];
// 检查两张牌都在手牌中 // 检查两张牌都在手牌中
if (!playerHand.includes(aceCardId) || !playerHand.includes(otherCardId)) { if (!playerHand.includes(aceCardId) || !playerHand.includes(otherCardId)) {
return {success: false, error: '卡牌不在手牌中'}; return { success: false, error: "卡牌不在手牌中" };
} }
if (!state.currentEnemy) { if (!state.currentEnemy) {
return {success: false, error: '没有活跃的敌人'}; return { success: false, error: "没有活跃的敌人" };
} }
// 计算两张牌的总伤害 // 计算两张牌的总伤害
let totalDamage = aceCard.value + otherCard.value; let totalDamage = aceCard.value + otherCard.value;
// 如果另一张牌是梅花,双倍伤害 // 如果另一张牌是梅花,双倍伤害
if (otherCard.suit === 'clubs') { if (otherCard.suit === "clubs") {
totalDamage *= 2; totalDamage *= 2;
} }
let attackReduction = 0; let attackReduction = 0;
if (aceCard.suit === 'spades') { if (aceCard.suit === "spades") {
attackReduction += aceCard.value; attackReduction += aceCard.value;
} }
if (otherCard.suit === 'spades') { if (otherCard.suit === "spades") {
attackReduction += otherCard.value; attackReduction += otherCard.value;
} }
await game.produce(state => { await game.produceAsync((state) => {
// 对敌人造成伤害 // 对敌人造成伤害
state.currentEnemy!.hp -= totalDamage; state.currentEnemy!.hp -= totalDamage;
// 记录黑桃的攻击力降低 // 记录黑桃的攻击力降低
if (attackReduction > 0) { if (attackReduction > 0) {
state.currentEnemy!.value = Math.max(0, state.currentEnemy!.value - attackReduction); 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 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({ async function passCmd(game: RegicideGame, player: string) {
schema: 'pass <player:string>', // 即使让过,也会受到敌人反击(在回合结束时处理)
run: async (game: RegicideGame, player: string) => { return { success: true, result: { message: `${player} 让过` } };
// 即使让过,也会受到敌人反击(在回合结束时处理) }
return {success: true, result: {message: `${player} 让过`}};
}
});
/** /**
* - >= * - >=
*/ */
const enemyCounterattackCmd = registry.register({ async function enemyCounterattackCmd(
schema: 'counterattack <player:string> <discardCards:string[]>', game: RegicideGame,
run: async (game: RegicideGame, player: string, discardCards: string[]) => { player: string,
const state = game.value; discardCards: string[],
) {
const state = game.value;
if (!state.currentEnemy) { if (!state.currentEnemy) {
return {success: false, error: '没有活跃的敌人'}; return { success: false, error: "没有活跃的敌人" };
} }
const playerKey = player as PlayerType; const playerKey = player as PlayerType;
const playerHand = state.playerHands[playerKey]; const playerHand = state.playerHands[playerKey];
// 检查要弃的牌都在手牌中 // 检查要弃的牌都在手牌中
for (const cardId of discardCards) { for (const cardId of discardCards) {
if (!playerHand.includes(cardId)) { if (!playerHand.includes(cardId)) {
return {success: false, error: `卡牌 ${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
}
};
} }
}); }
// 计算弃牌的点数和
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({ async function checkEnemyDefeatedCmd(game: RegicideGame) {
schema: 'check-enemy', const state = game.value;
run: async (game: RegicideGame) => {
const state = game.value;
if (!state.currentEnemy) { if (!state.currentEnemy) {
return {success: false, error: '没有活跃的敌人'}; return { success: false as const, error: "没有活跃的敌人" };
} }
const defeated = state.currentEnemy.hp <= 0; const defeated = state.currentEnemy.hp <= 0;
if (defeated) { if (defeated) {
const defeatedEnemy = {...state.currentEnemy}; const defeatedEnemy = { ...state.currentEnemy };
await game.produce(state => { await game.produceAsync((state) => {
// 将当前敌人移到弃牌堆 // 将当前敌人移到弃牌堆
state.regions.discardPile.childIds.push(state.currentEnemy!.id); state.regions.discardPile.childIds.push(state.currentEnemy!.id);
// 翻开下一个敌人 // 翻开下一个敌人
if (state.enemyDeck.length > 0) { if (state.enemyDeck.length > 0) {
const nextEnemy = state.enemyDeck.shift()!; const nextEnemy = state.enemyDeck.shift()!;
state.currentEnemy = nextEnemy; state.currentEnemy = nextEnemy;
} else { } else {
// 没有更多敌人了 // 没有更多敌人了
state.currentEnemy = null; state.currentEnemy = null;
} }
}); });
// 检查是否胜利(没有更多敌人) // 检查是否胜利(没有更多敌人)
if (!game.value.currentEnemy) { if (!game.value.currentEnemy) {
await game.produce(state => { await game.produceAsync((state) => {
state.phase = 'victory'; state.phase = "victory";
state.winner = true; 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
}
};
} }
});
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({ async function checkCanPlayCmd(game: RegicideGame, player: string) {
schema: 'check-can-play <player:string>', const state = game.value;
run: async (game: RegicideGame, player: string) => { const playerKey = player as PlayerType;
const state = game.value; const playerHand = state.playerHands[playerKey];
const playerKey = player as PlayerType;
const playerHand = state.playerHands[playerKey];
const canPlay = playerHand.length > 0; const canPlay = playerHand.length > 0;
const canPlayWithA = playerHand.some(cardId => { const canPlayWithA = playerHand.some((cardId) => {
const card = state.cards[cardId]; const card = state.cards[cardId];
return card && card.rank === 'A' && playerHand.length > 1; return card && card.rank === "A" && playerHand.length > 1;
}); });
return { return {
success: true, success: true,
result: { result: {
canPlay, canPlay,
canPlayWithA, canPlayWithA,
handSize: playerHand.length handSize: playerHand.length,
} },
}; };
} }
});
/** /**
* *
*/ */
const checkTavernDeckCmd = registry.register({ async function checkTavernDeckCmd(game: RegicideGame) {
schema: 'check-tavern-deck', const state = game.value;
run: async (game: RegicideGame) => { const isEmpty = state.regions.tavernDeck.childIds.length === 0;
const state = game.value;
const isEmpty = state.regions.tavernDeck.childIds.length === 0;
// 如果酒馆牌堆为空且所有玩家手牌也为空,则游戏失败 // 如果酒馆牌堆为空且所有玩家手牌也为空,则游戏失败
if (isEmpty) { if (isEmpty) {
const allHandsEmpty = Object.values(state.playerHands).every(hand => hand.length === 0); const allHandsEmpty = Object.values(state.playerHands).every(
if (allHandsEmpty) { (hand) => hand.length === 0,
await game.produce(state => { );
state.phase = 'defeat'; if (allHandsEmpty) {
state.winner = false; await game.produceAsync((state) => {
}); state.phase = "defeat";
} state.winner = false;
} });
return {
success: true,
result: {
isEmpty,
cardsRemaining: state.regions.tavernDeck.childIds.length
}
};
} }
}); }
return {
success: true,
result: {
isEmpty,
cardsRemaining: state.regions.tavernDeck.childIds.length,
},
};
}
/** /**
* *
*/ */
const nextTurnCmd = registry.register({ async function nextTurnCmd(game: RegicideGame) {
schema: 'next-turn', const state = game.value;
run: async (game: RegicideGame) => { await game.produce((state) => {
const state = game.value; state.currentPlayerIndex =
await game.produce(state => { (state.currentPlayerIndex + 1) % state.playerCount;
state.currentPlayerIndex = (state.currentPlayerIndex + 1) % state.playerCount; });
});
const players: PlayerType[] = ['player1', 'player2', 'player3', 'player4']; const players: PlayerType[] = ["player1", "player2", "player3", "player4"];
const currentPlayer = players[game.value.currentPlayerIndex]; const currentPlayer = players[game.value.currentPlayerIndex];
return { return {
success: true, success: true,
result: { result: {
currentPlayer, currentPlayer,
currentPlayerIndex: game.value.currentPlayerIndex currentPlayerIndex: game.value.currentPlayerIndex,
} },
}; };
} }
});
export { export {
playCmd as play, playCmd as play,
playWithACmd as playWithA, playWithACmd as playWithA,
passCmd as pass, passCmd as pass,
enemyCounterattackCmd as enemyCounterattack, enemyCounterattackCmd as enemyCounterattack,
checkEnemyDefeatedCmd as checkEnemy, checkEnemyDefeatedCmd as checkEnemy,
checkCanPlayCmd as checkCanPlay, checkCanPlayCmd as checkCanPlay,
checkTavernDeckCmd as checkTavernDeck, checkTavernDeckCmd as checkTavernDeck,
nextTurnCmd as nextTurn, nextTurnCmd as nextTurn,
}; };

View File

@ -1,8 +1,15 @@
import {IGameContext} from "@/core/game"; import { IGameContext } from "@/core/game";
import {RegicideState} from "@/samples/regicide/state"; import { RegicideState } from "@/samples/regicide/state";
import {buildEnemyDeck, buildTavernDeck, createAllCards, getPlayerHandRegionId} from "@/samples/regicide/utils"; import {
import {INITIAL_HAND_SIZE} from "@/samples/regicide/constants"; buildEnemyDeck,
import {Enemy, PlayerType, RegicideCard} from "@/samples/regicide/types"; 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<RegicideState>; export type RegicideGame = IGameContext<RegicideState>;
@ -12,228 +19,255 @@ export type RegicideGame = IGameContext<RegicideState>;
* @param playerCount 1-4 * @param playerCount 1-4
* @param seed * @param seed
*/ */
export async function setupGame(game: RegicideGame, playerCount: number, seed?: number) { export async function setupGame(
if (playerCount < 1 || playerCount > 4) { game: RegicideGame,
throw new Error('玩家数量必须为 1-4 人'); 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) { // 设置敌人牌堆区域只存储ID敌人是独立对象
// RNG seeding handled by game context 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(); if (enemyDeck.length > 0) {
const firstEnemy = enemyDeck.shift()!;
// 构建敌人牌堆J/Q/K state.currentEnemy = firstEnemy;
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;
}
});
} }
/** /**
* *
*/ */
export async function start(game: RegicideGame) { export async function start(game: RegicideGame) {
const state = game.value; const state = game.value;
// 检查游戏是否已设置 // 检查游戏是否已设置
if (!state.currentEnemy) { if (!state.currentEnemy) {
throw new Error('请先调用 setupGame 初始化游戏'); 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 或测试代码提供输入
// 主游戏循环 // 循环会在外部调用 play/pass 命令后继续
while (state.phase === 'playing') { // 当 phase 变为 'victory' 或 'defeat' 时退出
const currentPlayerIndex = state.currentPlayerIndex; break;
const currentPlayer = players[currentPlayerIndex]; }
// 检查当前玩家是否有手牌 return game.value;
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;
} }
/** /**
* *
*/ */
export async function playTurn(game: RegicideGame, player: PlayerType, action: 'play' | 'pass', cardId?: string, secondCardId?: string) { export async function playTurn(
const state = game.value; game: RegicideGame,
player: PlayerType,
action: "play" | "pass",
cardId?: string,
secondCardId?: string,
) {
const state = game.value;
if (state.phase !== 'playing') { if (state.phase !== "playing") {
return {success: false, error: '游戏已结束'}; return { success: false, error: "游戏已结束" };
} }
if (!state.currentEnemy) { if (!state.currentEnemy) {
return {success: false, error: '没有活跃的敌人'}; return { success: false, error: "没有活跃的敌人" };
} }
let playResult: any; let playResult: any;
// 执行玩家动作 // 执行玩家动作
if (action === 'play' && cardId) { if (action === "play" && cardId) {
// 检查是否是A配合另一张牌 // 检查是否是A配合另一张牌
const card = state.cards[cardId]; const card = state.cards[cardId];
if (card.rank === 'A' && secondCardId) { if (card.rank === "A" && secondCardId) {
playResult = await game.run(`play-with-a ${player} ${cardId} ${secondCardId}`); playResult = await playWithA(game, player, cardId, secondCardId);
} else {
playResult = await game.run(`play ${player} ${cardId}`);
}
} else { } else {
// 让过 playResult = await playCmd(game, player, cardId);
playResult = await game.run(`pass ${player}`);
} }
} else {
// 让过
playResult = await pass(game, player);
}
if (!playResult.success) { if (!playResult.success) {
return playResult; return playResult;
} }
// 检查敌人是否被击败 // 检查敌人是否被击败
const checkResult = await game.run<{defeated: boolean; currentEnemy?: any; nextEnemy?: any; defeatedEnemy?: any; enemiesRemaining?: number}>('check-enemy'); const checkResult = await checkEnemy(game);
if (!checkResult.success) { if (!checkResult.success) {
return checkResult; 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');
// 如果敌人未被击败,处理反击
if (!checkResult.result.defeated) {
// 反击逻辑需要玩家选择弃牌,这里返回状态让外部处理
return { return {
success: true, success: true,
result: { result: {
playResult: playResult.result, playResult: playResult.result,
enemyDefeated: true, enemyDefeated: false,
nextEnemy: state.currentEnemy 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[]) { export async function handleCounterattack(
const result = await game.run(`counterattack ${player} ${JSON.stringify(discardCardIds)}`); game: RegicideGame,
player: PlayerType,
if (!result.success) { discardCardIds: string[],
// 弃牌失败(点数和不足),游戏失败 ) {
await game.produceAsync(state => { const result = await enemyCounterattackCmd(game, player, discardCardIds);
state.phase = 'defeat'; if (!result.success) {
state.winner = false; // 弃牌失败(点数和不足),游戏失败
}); await game.produceAsync((state) => {
return result; state.phase = "defeat";
} state.winner = false;
});
// 弃牌成功,切换到下一个玩家
await game.run('next-turn');
return result; return result;
}
// 弃牌成功,切换到下一个玩家
await nextTurn(game);
return result;
} }
/** /**
* *
*/ */
export function getGameStatus(game: RegicideGame) { export function getGameStatus(game: RegicideGame) {
const state = game.value; const state = game.value;
return { return {
phase: state.phase, phase: state.phase,
currentPlayer: ['player1', 'player2', 'player3', 'player4'][state.currentPlayerIndex], currentPlayer: ["player1", "player2", "player3", "player4"][
currentEnemy: state.currentEnemy ? { state.currentPlayerIndex
...state.currentEnemy, ],
hpPercent: Math.round((state.currentEnemy.hp / state.currentEnemy.maxHp) * 100) currentEnemy: state.currentEnemy
} : null, ? {
enemiesRemaining: state.enemyDeck.length, ...state.currentEnemy,
tavernDeckCount: state.regions.tavernDeck.childIds.length, hpPercent: Math.round(
discardPileCount: state.regions.discardPile.childIds.length, (state.currentEnemy.hp / state.currentEnemy.maxHp) * 100,
playerHands: Object.fromEntries( ),
Object.entries(state.playerHands).map(([player, hand]) => [player, hand.length]) }
), : null,
winner: state.winner 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,
};
} }