|
@@ -0,0 +1,421 @@
|
|
|
+import { defineStore } from 'pinia'
|
|
|
+import { werewolfAPI } from '@/services/api/games/werewolf'
|
|
|
+
|
|
|
+// 玩家角色类型
|
|
|
+export type WerewolfRole =
|
|
|
+ | 'werewolf' // 狼人
|
|
|
+ | 'villager' // 村民
|
|
|
+ | 'seer' // 预言家
|
|
|
+ | 'witch' // 女巫
|
|
|
+ | 'hunter' // 猎人
|
|
|
+ | 'guard' // 守卫
|
|
|
+ | 'idiot' // 白痴
|
|
|
+ | 'elder' // 长老
|
|
|
+ | 'cupid' // 丘比特
|
|
|
+ | 'thief' // 盗贼
|
|
|
+ | 'moderator'; // 主持人/上帝
|
|
|
+
|
|
|
+// 游戏阶段
|
|
|
+export type GamePhase =
|
|
|
+ | 'waiting' // 等待开始
|
|
|
+ | 'night' // 夜晚
|
|
|
+ | 'day' // 白天
|
|
|
+ | 'vote' // 投票
|
|
|
+ | 'ended'; // 游戏结束
|
|
|
+
|
|
|
+// 行动类型
|
|
|
+export type ActionType =
|
|
|
+ | 'kill' // 狼人杀人
|
|
|
+ | 'check' // 预言家查验
|
|
|
+ | 'save' // 女巫救人
|
|
|
+ | 'poison' // 女巫毒人
|
|
|
+ | 'protect' // 守卫保护
|
|
|
+ | 'shoot' // 猎人开枪
|
|
|
+ | 'vote' // 投票
|
|
|
+ | 'pair'; // 丘比特连线
|
|
|
+
|
|
|
+// 玩家状态
|
|
|
+export type PlayerStatus =
|
|
|
+ | 'alive' // 存活
|
|
|
+ | 'dead' // 死亡
|
|
|
+ | 'protected' // 受保护
|
|
|
+ | 'poisoned' // 中毒
|
|
|
+ | 'coupled'; // 情侣
|
|
|
+
|
|
|
+// 游戏结果类型
|
|
|
+export type GameResult =
|
|
|
+ | 'werewolf_win' // 狼人阵营胜利
|
|
|
+ | 'villager_win' // 村民阵营胜利
|
|
|
+ | 'couple_win' // 情侣胜利
|
|
|
+ | 'draw' // 平局
|
|
|
+ | null; // 游戏进行中
|
|
|
+
|
|
|
+// 玩家信息
|
|
|
+export interface WerewolfPlayer {
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ avatar: string;
|
|
|
+ role: WerewolfRole;
|
|
|
+ status: PlayerStatus;
|
|
|
+ isReady: boolean;
|
|
|
+ isDead: boolean;
|
|
|
+ actions: {
|
|
|
+ [key in ActionType]?: boolean;
|
|
|
+ };
|
|
|
+ // 特殊状态
|
|
|
+ isLovers?: boolean; // 是否为情侣
|
|
|
+ lastWords?: string; // 遗言
|
|
|
+ voteTarget?: string; // 投票目标
|
|
|
+ deadRound?: number; // 死亡回合
|
|
|
+}
|
|
|
+
|
|
|
+// 游戏日志
|
|
|
+export interface GameLog {
|
|
|
+ round: number;
|
|
|
+ phase: GamePhase;
|
|
|
+ content: string;
|
|
|
+ timestamp: number;
|
|
|
+ isPublic: boolean;
|
|
|
+ targetPlayers?: string[];
|
|
|
+}
|
|
|
+
|
|
|
+// 游戏状态
|
|
|
+export interface WerewolfGame {
|
|
|
+ id: string;
|
|
|
+ title: string;
|
|
|
+ hosterId: string;
|
|
|
+ hosterName: string;
|
|
|
+ createTime: number;
|
|
|
+ startTime: number | null;
|
|
|
+ endTime: number | null;
|
|
|
+ players: WerewolfPlayer[];
|
|
|
+ currentRound: number;
|
|
|
+ phase: GamePhase;
|
|
|
+ result: GameResult;
|
|
|
+ currentAction: ActionType | null;
|
|
|
+ timeLimit: number; // 每个阶段的时间限制(秒)
|
|
|
+ logs: GameLog[];
|
|
|
+ options: {
|
|
|
+ roles: {
|
|
|
+ [key in WerewolfRole]?: number; // 角色数量配置
|
|
|
+ };
|
|
|
+ includeSpecialRoles: boolean; // 是否包含特殊角色
|
|
|
+ allowLastWords: boolean; // 是否允许遗言
|
|
|
+ nightTimeLimit: number; // 夜晚时间限制
|
|
|
+ dayTimeLimit: number; // 白天时间限制
|
|
|
+ voteTimeLimit: number; // 投票时间限制
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+// 定义 Store
|
|
|
+export const useWerewolfStore = defineStore('werewolf', {
|
|
|
+ state: () => ({
|
|
|
+ currentGame: null as WerewolfGame | null,
|
|
|
+ loading: false,
|
|
|
+ error: null as string | null,
|
|
|
+ currentPlayer: null as WerewolfPlayer | null,
|
|
|
+ timer: null as number | null,
|
|
|
+ remainingTime: 0,
|
|
|
+ myRole: null as WerewolfRole | null,
|
|
|
+ myActions: [] as ActionType[],
|
|
|
+ gameResult: null as GameResult,
|
|
|
+ showRoleInfo: false,
|
|
|
+ showActionInfo: false
|
|
|
+ }),
|
|
|
+
|
|
|
+ getters: {
|
|
|
+ // 获取活着的玩家
|
|
|
+ alivePlayers: (state) => {
|
|
|
+ if (!state.currentGame) return [];
|
|
|
+ return state.currentGame.players.filter(p => !p.isDead);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取死亡的玩家
|
|
|
+ deadPlayers: (state) => {
|
|
|
+ if (!state.currentGame) return [];
|
|
|
+ return state.currentGame.players.filter(p => p.isDead);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取狼人玩家
|
|
|
+ werewolfPlayers: (state) => {
|
|
|
+ if (!state.currentGame) return [];
|
|
|
+ return state.currentGame.players.filter(p => p.role === 'werewolf');
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取村民阵营玩家
|
|
|
+ villagerTeamPlayers: (state) => {
|
|
|
+ if (!state.currentGame) return [];
|
|
|
+ return state.currentGame.players.filter(p =>
|
|
|
+ p.role !== 'werewolf' && p.role !== 'moderator'
|
|
|
+ );
|
|
|
+ },
|
|
|
+
|
|
|
+ // 当前玩家是否是狼人
|
|
|
+ isWerewolf: (state) => {
|
|
|
+ return state.myRole === 'werewolf';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 是否是夜晚
|
|
|
+ isNightPhase: (state) => {
|
|
|
+ return state.currentGame?.phase === 'night';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 是否是白天
|
|
|
+ isDayPhase: (state) => {
|
|
|
+ return state.currentGame?.phase === 'day';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 是否是投票阶段
|
|
|
+ isVotePhase: (state) => {
|
|
|
+ return state.currentGame?.phase === 'vote';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 游戏是否结束
|
|
|
+ isGameEnded: (state) => {
|
|
|
+ return state.currentGame?.phase === 'ended';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 当前玩家是否可以行动
|
|
|
+ canAct: (state) => {
|
|
|
+ if (!state.currentGame || !state.myRole || !state.currentPlayer) return false;
|
|
|
+ if (state.currentPlayer.isDead) return false;
|
|
|
+
|
|
|
+ const { phase, currentAction } = state.currentGame;
|
|
|
+
|
|
|
+ // 根据游戏阶段和角色确定是否可以行动
|
|
|
+ if (phase === 'night') {
|
|
|
+ switch (currentAction) {
|
|
|
+ case 'kill': return state.myRole === 'werewolf';
|
|
|
+ case 'check': return state.myRole === 'seer';
|
|
|
+ case 'save':
|
|
|
+ case 'poison': return state.myRole === 'witch';
|
|
|
+ case 'protect': return state.myRole === 'guard';
|
|
|
+ case 'pair': return state.myRole === 'cupid';
|
|
|
+ default: return false;
|
|
|
+ }
|
|
|
+ } else if (phase === 'vote') {
|
|
|
+ return !state.currentPlayer.voteTarget;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 获取当前可选择的目标玩家
|
|
|
+ availableTargets: (state) => {
|
|
|
+ if (!state.currentGame || !state.myRole) return [];
|
|
|
+
|
|
|
+ const { phase, currentAction, players } = state.currentGame;
|
|
|
+
|
|
|
+ // 根据不同行动类型过滤可选目标
|
|
|
+ if (phase === 'night') {
|
|
|
+ switch (currentAction) {
|
|
|
+ case 'kill': // 狼人只能杀死非狼人
|
|
|
+ return players.filter(p => !p.isDead && p.role !== 'werewolf');
|
|
|
+ case 'check': // 预言家查验活着的玩家
|
|
|
+ return players.filter(p => !p.isDead);
|
|
|
+ case 'save': // 女巫只能救死亡的玩家
|
|
|
+ return players.filter(p => p.status === 'poisoned');
|
|
|
+ case 'poison': // 女巫只能毒活着的玩家
|
|
|
+ return players.filter(p => !p.isDead);
|
|
|
+ case 'protect': // 守卫只能保护活着的玩家
|
|
|
+ return players.filter(p => !p.isDead);
|
|
|
+ default:
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ } else if (phase === 'vote') {
|
|
|
+ // 投票阶段可以选择所有活着的玩家(除了自己)
|
|
|
+ return players.filter(p => !p.isDead && p.id !== state.currentPlayer?.id);
|
|
|
+ }
|
|
|
+
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ actions: {
|
|
|
+ // 加载游戏数据
|
|
|
+ async loadGame(gameId: string, role: string) {
|
|
|
+ this.loading = true;
|
|
|
+ this.error = null;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const result = await werewolfAPI.getGameData(gameId, role);
|
|
|
+ if (result && result.game) {
|
|
|
+ this.currentGame = result.game;
|
|
|
+
|
|
|
+ // 找到当前玩家
|
|
|
+ if (this.currentGame.players) {
|
|
|
+ // 这里需要根据实际情况从某处获取当前用户ID
|
|
|
+ const currentUserId = 'current_user_id'; // 应替换为实际的用户ID获取方式
|
|
|
+ this.currentPlayer = this.currentGame.players.find(p => p.id === currentUserId) || null;
|
|
|
+ this.myRole = this.currentPlayer?.role || null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.error = '加载游戏数据失败';
|
|
|
+ return false;
|
|
|
+ } catch (err) {
|
|
|
+ console.error('加载狼人杀游戏失败:', err);
|
|
|
+ this.error = err instanceof Error ? err.message : '未知错误';
|
|
|
+ return false;
|
|
|
+ } finally {
|
|
|
+ this.loading = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 准备游戏
|
|
|
+ async readyGame() {
|
|
|
+ if (!this.currentGame || !this.currentPlayer) return false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ await werewolfAPI.playerAction(
|
|
|
+ this.currentGame.id,
|
|
|
+ this.currentPlayer.id,
|
|
|
+ 'ready'
|
|
|
+ );
|
|
|
+ return true;
|
|
|
+ } catch (err) {
|
|
|
+ console.error('准备游戏失败:', err);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 玩家行动
|
|
|
+ async performAction(action: ActionType, targetId?: string) {
|
|
|
+ if (!this.currentGame || !this.currentPlayer) return false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ await werewolfAPI.playerAction(
|
|
|
+ this.currentGame.id,
|
|
|
+ this.currentPlayer.id,
|
|
|
+ action,
|
|
|
+ targetId
|
|
|
+ );
|
|
|
+ return true;
|
|
|
+ } catch (err) {
|
|
|
+ console.error('执行行动失败:', err);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 狼人杀人
|
|
|
+ async killPlayer(targetId: string) {
|
|
|
+ return this.performAction('kill', targetId);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 预言家查验
|
|
|
+ async checkPlayer(targetId: string) {
|
|
|
+ return this.performAction('check', targetId);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 女巫救人
|
|
|
+ async savePlayer(targetId: string) {
|
|
|
+ return this.performAction('save', targetId);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 女巫毒人
|
|
|
+ async poisonPlayer(targetId: string) {
|
|
|
+ return this.performAction('poison', targetId);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 守卫保护
|
|
|
+ async protectPlayer(targetId: string) {
|
|
|
+ return this.performAction('protect', targetId);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 投票
|
|
|
+ async votePlayer(targetId: string) {
|
|
|
+ return this.performAction('vote', targetId);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 猎人开枪
|
|
|
+ async shootPlayer(targetId: string) {
|
|
|
+ return this.performAction('shoot', targetId);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 丘比特连线
|
|
|
+ async couplePlayer(player1Id: string, player2Id: string) {
|
|
|
+ try {
|
|
|
+ await werewolfAPI.playerAction(
|
|
|
+ this.currentGame!.id,
|
|
|
+ this.currentPlayer!.id,
|
|
|
+ 'pair',
|
|
|
+ JSON.stringify([player1Id, player2Id])
|
|
|
+ );
|
|
|
+ return true;
|
|
|
+ } catch (err) {
|
|
|
+ console.error('丘比特连线失败:', err);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 留遗言
|
|
|
+ async leaveLastWords(message: string) {
|
|
|
+ if (!this.currentGame || !this.currentPlayer) return false;
|
|
|
+
|
|
|
+ try {
|
|
|
+ await werewolfAPI.playerAction(
|
|
|
+ this.currentGame.id,
|
|
|
+ this.currentPlayer.id,
|
|
|
+ 'lastWords',
|
|
|
+ message
|
|
|
+ );
|
|
|
+ return true;
|
|
|
+ } catch (err) {
|
|
|
+ console.error('留遗言失败:', err);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 开始倒计时
|
|
|
+ startTimer(seconds: number) {
|
|
|
+ this.remainingTime = seconds;
|
|
|
+
|
|
|
+ // 清除现有的计时器
|
|
|
+ if (this.timer !== null) {
|
|
|
+ clearInterval(this.timer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置新的计时器
|
|
|
+ this.timer = window.setInterval(() => {
|
|
|
+ if (this.remainingTime > 0) {
|
|
|
+ this.remainingTime--;
|
|
|
+ } else {
|
|
|
+ if (this.timer !== null) {
|
|
|
+ clearInterval(this.timer);
|
|
|
+ this.timer = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, 1000);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 停止倒计时
|
|
|
+ stopTimer() {
|
|
|
+ if (this.timer !== null) {
|
|
|
+ clearInterval(this.timer);
|
|
|
+ this.timer = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 显示角色信息
|
|
|
+ showRoleInformation() {
|
|
|
+ this.showRoleInfo = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 隐藏角色信息
|
|
|
+ hideRoleInformation() {
|
|
|
+ this.showRoleInfo = false;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 清理游戏数据
|
|
|
+ clearGame() {
|
|
|
+ this.currentGame = null;
|
|
|
+ this.currentPlayer = null;
|
|
|
+ this.myRole = null;
|
|
|
+ this.myActions = [];
|
|
|
+ this.gameResult = null;
|
|
|
+ this.stopTimer();
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|