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
+219 -185
View File
@@ -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<RegicideState>;
@@ -12,228 +19,255 @@ export type RegicideGame = IGameContext<RegicideState>;
* @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,
};
}