Commit 4ad6b82a authored by timel's avatar timel

feat: remove all unused dependencies

parent 9bbb4813
......@@ -14,7 +14,6 @@
"@react-spring/shared": "^9.7.2",
"@react-spring/types": "^9.7.2",
"@react-spring/web": "^9.7.2",
"@reduxjs/toolkit": "^1.9.3",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
......@@ -22,7 +21,6 @@
"@types/node": "^16.18.23",
"@types/react": "^18.0.33",
"@types/react-dom": "^18.0.11",
"@types/react-redux": "^7.1.25",
"@types/sql.js": "^1.4.4",
"antd": "^5.4.0",
"axios": "^0.27.2",
......@@ -32,7 +30,6 @@
"react-babylonjs": "^3.1.15",
"react-dom": "^18.2.0",
"react-draggable": "^4.4.5",
"react-redux": "^8.0.5",
"react-router-dom": "^6.10.0",
"react-scripts": "^2.1.3",
"socket.io-client": "^4.6.1",
......@@ -3180,29 +3177,6 @@
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/@reduxjs/toolkit": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.3.tgz",
"integrity": "sha512-GU2TNBQVofL09VGmuSioNPQIu6Ml0YLf4EJhgj0AvBadRlCGzUWet8372LjvO4fqKZF2vH1xU0htAa7BrK9pZg==",
"dependencies": {
"immer": "^9.0.16",
"redux": "^4.2.0",
"redux-thunk": "^2.4.2",
"reselect": "^4.1.7"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18",
"react-redux": "^7.2.1 || ^8.0.2"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@remix-run/router": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.5.0.tgz",
......@@ -3628,15 +3602,6 @@
"integrity": "sha512-pYVNNJ+winC4aek+lZp93sIKxnXt5qMkuKmaqS3WGuTq0Bw1ZDYNBgzG5kkdtwcv+GmYJGo3yEg6z2cKKAiEdw==",
"dev": true
},
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/jest": {
"version": "27.5.2",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz",
......@@ -3711,17 +3676,6 @@
"@types/react": "*"
}
},
"node_modules/@types/react-redux": {
"version": "7.1.25",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.25.tgz",
"integrity": "sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==",
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0",
"redux": "^4.0.0"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
......@@ -3755,11 +3709,6 @@
"@types/jest": "*"
}
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.57.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.57.1.tgz",
......@@ -11005,14 +10954,6 @@
"node": ">=4.0.0"
}
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
......@@ -11568,15 +11509,6 @@
"node": ">= 4"
}
},
"node_modules/immer": {
"version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/immutable": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz",
......@@ -21412,49 +21344,6 @@
"loose-envify": "^1.1.0"
}
},
"node_modules/react-redux": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
"integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==",
"dependencies": {
"@babel/runtime": "^7.12.1",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/use-sync-external-store": "^0.0.3",
"hoist-non-react-statics": "^3.3.2",
"react-is": "^18.0.0",
"use-sync-external-store": "^1.0.0"
},
"peerDependencies": {
"@types/react": "^16.8 || ^17.0 || ^18.0",
"@types/react-dom": "^16.8 || ^17.0 || ^18.0",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0",
"react-native": ">=0.59",
"redux": "^4"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
},
"react-dom": {
"optional": true
},
"react-native": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-redux/node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
},
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
......@@ -23068,22 +22957,6 @@
"node": ">=8"
}
},
"node_modules/redux": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz",
"integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==",
"dependencies": {
"@babel/runtime": "^7.9.2"
}
},
"node_modules/redux-thunk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
"integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
"peerDependencies": {
"redux": "^4"
}
},
"node_modules/regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
......@@ -23453,11 +23326,6 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
},
"node_modules/reselect": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz",
"integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A=="
},
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
......@@ -30955,17 +30823,6 @@
"@react-spring/types": "~9.7.2"
}
},
"@reduxjs/toolkit": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.3.tgz",
"integrity": "sha512-GU2TNBQVofL09VGmuSioNPQIu6Ml0YLf4EJhgj0AvBadRlCGzUWet8372LjvO4fqKZF2vH1xU0htAa7BrK9pZg==",
"requires": {
"immer": "^9.0.16",
"redux": "^4.2.0",
"redux-thunk": "^2.4.2",
"reselect": "^4.1.7"
}
},
"@remix-run/router": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.5.0.tgz",
......@@ -31234,15 +31091,6 @@
"integrity": "sha512-pYVNNJ+winC4aek+lZp93sIKxnXt5qMkuKmaqS3WGuTq0Bw1ZDYNBgzG5kkdtwcv+GmYJGo3yEg6z2cKKAiEdw==",
"dev": true
},
"@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"requires": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/jest": {
"version": "27.5.2",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz",
......@@ -31317,17 +31165,6 @@
"@types/react": "*"
}
},
"@types/react-redux": {
"version": "7.1.25",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.25.tgz",
"integrity": "sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==",
"requires": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0",
"redux": "^4.0.0"
}
},
"@types/scheduler": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
......@@ -31361,11 +31198,6 @@
"@types/jest": "*"
}
},
"@types/use-sync-external-store": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="
},
"@typescript-eslint/eslint-plugin": {
"version": "5.57.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.57.1.tgz",
......@@ -37046,14 +36878,6 @@
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
"integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA=="
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"requires": {
"react-is": "^16.7.0"
}
},
"home-or-tmp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
......@@ -37487,11 +37311,6 @@
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ=="
},
"immer": {
"version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA=="
},
"immutable": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz",
......@@ -45103,26 +44922,6 @@
}
}
},
"react-redux": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
"integrity": "sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==",
"requires": {
"@babel/runtime": "^7.12.1",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/use-sync-external-store": "^0.0.3",
"hoist-non-react-statics": "^3.3.2",
"react-is": "^18.0.0",
"use-sync-external-store": "^1.0.0"
},
"dependencies": {
"react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
}
}
},
"react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
......@@ -46362,20 +46161,6 @@
"strip-indent": "^3.0.0"
}
},
"redux": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz",
"integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==",
"requires": {
"@babel/runtime": "^7.9.2"
}
},
"redux-thunk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
"integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
"requires": {}
},
"regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
......@@ -46662,11 +46447,6 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
},
"reselect": {
"version": "4.1.7",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz",
"integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A=="
},
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
export * from "./useApp";
export * from "./useEnv";
export * from "./useMeshClick";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { AppDispatch, RootState } from "@/store";
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
......@@ -23,10 +23,8 @@ import { ConfigProvider, theme } from "antd";
import zhCN from "antd/locale/zh_CN";
import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import { BrowserRouter } from "react-router-dom";
import { store } from "./store";
import Neos from "./ui/Neos";
const root = ReactDOM.createRoot(
......@@ -35,14 +33,9 @@ const root = ReactDOM.createRoot(
root.render(
<React.StrictMode>
<BrowserRouter>
<Provider store={store}>
<ConfigProvider
theme={{ algorithm: theme.darkAlgorithm }}
locale={zhCN}
>
<Neos />
</ConfigProvider>
</Provider>
<ConfigProvider theme={{ algorithm: theme.darkAlgorithm }} locale={zhCN}>
<Neos />
</ConfigProvider>
</BrowserRouter>
</React.StrictMode>
);
/*
* Chat状态更新逻辑
*
* */
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "@/store";
export interface chatState {
message: string;
}
const initialState: chatState = {
message: "",
};
const chatSlice = createSlice({
name: "chat",
initialState,
reducers: {
postChat: (state, action: PayloadAction<string>) => {
state.message = action.payload;
},
},
});
export const { postChat } = chatSlice.actions;
export const selectChat = (state: RootState) => state.chat.message;
export default chatSlice.reducer;
import {
ActionReducerMapBuilder,
CaseReducer,
PayloadAction,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { RootState } from "@/store";
import {
createAsyncMetaThunk,
DuelFieldState,
DuelReducer,
extendIdleInteractivities,
extendMeta,
extendState,
Interactivity,
removeCard,
} from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface BanishedZoneState extends DuelFieldState {}
// 初始化除外区状态
export const initBanishedZoneImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
const player = action.payload;
if (judgeSelf(player, state)) {
state.meBanishedZone = { inner: [] };
} else {
state.opBanishedZone = { inner: [] };
}
};
// 增加除外区
export const fetchBanishedZoneMeta = createAsyncMetaThunk(
"duel/fetchBanishedZoneMeta"
);
export const banishedZoneCase = (
builder: ActionReducerMapBuilder<DuelState>
) => {
builder.addCase(fetchBanishedZoneMeta.pending, (state, action) => {
// Meta结果没返回之前先更新`ID`
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
const code = action.meta.arg.code;
const newExclusion = {
occupant: { id: code, data: {}, text: {} },
location: {
controler,
location: ygopro.CardZone.REMOVED,
},
idleInteractivities: [],
counters: {},
};
if (judgeSelf(controler, state)) {
extendState(state.meBanishedZone, newExclusion, sequence);
} else {
extendState(state.opBanishedZone, newExclusion, sequence);
}
});
builder.addCase(fetchBanishedZoneMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const meta = action.payload.meta;
if (judgeSelf(controler, state)) {
extendMeta(state.meBanishedZone, meta, sequence);
} else {
extendMeta(state.opBanishedZone, meta, sequence);
}
});
};
// 删除除外区
export const removeBanishedZoneImpl: CaseReducer<
DuelState,
PayloadAction<{ controler: number; sequence: number }>
> = (state, action) => {
const banishedZone = judgeSelf(action.payload.controler, state)
? state.meBanishedZone
: state.opBanishedZone;
removeCard(banishedZone, action.payload.sequence);
};
export const addBanishedZoneIdleInteractivitiesImpl: DuelReducer<{
player: number;
sequence: number;
interactivity: Interactivity<number>;
}> = (state, action) => {
const banishedZone = judgeSelf(action.payload.player, state)
? state.meBanishedZone
: state.opBanishedZone;
extendIdleInteractivities(
banishedZone,
action.payload.sequence,
action.payload.interactivity
);
};
export const selectMeBanishedZone = (state: RootState) =>
state.duel.meBanishedZone || { inner: [] };
export const selectOpBanishedZone = (state: RootState) =>
state.duel.opBanishedZone || { inner: [] };
import { ygopro } from "@/api";
import {
clearIdleInteractivities,
clearPlaceInteractivities,
DuelReducer,
reloadFieldMeta,
updateCardData,
} from "./generic";
import { judgeSelf } from "./util";
import MsgReloadField = ygopro.StocGameMessage.MsgReloadField;
type MsgUpdateData = ReturnType<
typeof ygopro.StocGameMessage.MsgUpdateData.prototype.toObject
>;
export const clearAllIdleInteractivitiesImpl: DuelReducer<number> = (
state,
action
) => {
const player = action.payload;
const states = judgeSelf(player, state)
? [
state.meHands,
state.meMonsters,
state.meMagics,
state.meGraveyard,
state.meBanishedZone,
state.meExtraDeck,
]
: [
state.opHands,
state.opMonsters,
state.opMagics,
state.opGraveyard,
state.opBanishedZone,
state.opExtraDeck,
];
states.forEach((item) => clearIdleInteractivities(item));
};
export const clearAllPlaceInteractivitiesImpl: DuelReducer<number> = (
state,
action
) => {
const player = action.payload;
const states = judgeSelf(player, state)
? [
state.meHands,
state.meMonsters,
state.meMagics,
state.meGraveyard,
state.meBanishedZone,
]
: [
state.opHands,
state.opMonsters,
state.opMagics,
state.opGraveyard,
state.opBanishedZone,
];
states.forEach((item) => clearPlaceInteractivities(item));
};
export const updateFieldDataImpl: DuelReducer<MsgUpdateData> = (
state,
action
) => {
const player = action.payload.player;
const zone = action.payload.zone;
const actions = action.payload.actions;
if (player !== undefined && zone !== undefined && actions !== undefined) {
switch (zone) {
case ygopro.CardZone.HAND: {
const hand = judgeSelf(player, state) ? state.meHands : state.opHands;
updateCardData(hand, actions);
break;
}
case ygopro.CardZone.EXTRA: {
const extra = judgeSelf(player, state)
? state.meExtraDeck
: state.opExtraDeck;
updateCardData(extra, actions);
break;
}
case ygopro.CardZone.MZONE: {
const monster = judgeSelf(player, state)
? state.meMonsters
: state.opMonsters;
updateCardData(monster, actions);
break;
}
case ygopro.CardZone.SZONE: {
const magics = judgeSelf(player, state)
? state.meMagics
: state.opMagics;
updateCardData(magics, actions);
break;
}
case ygopro.CardZone.GRAVE: {
const graveyard = judgeSelf(player, state)
? state.meGraveyard
: state.opGraveyard;
updateCardData(graveyard, actions);
break;
}
case ygopro.CardZone.REMOVED: {
const BanishedZone = judgeSelf(player, state)
? state.meBanishedZone
: state.opBanishedZone;
updateCardData(BanishedZone, actions);
break;
}
default: {
break;
}
}
}
};
export const reloadFieldImpl: DuelReducer<MsgReloadField> = (state, action) => {
const _duel_rule = action.payload.duel_rule;
// 初始化`DuelState`
state.meDeck = { inner: [] };
state.opDeck = { inner: [] };
state.meExtraDeck = { inner: [] };
state.opExtraDeck = { inner: [] };
state.meMonsters = { inner: [] };
state.opMonsters = { inner: [] };
state.meMagics = { inner: [] };
state.opMagics = { inner: [] };
state.meGraveyard = { inner: [] };
state.opGraveyard = { inner: [] };
state.meBanishedZone = { inner: [] };
state.opBanishedZone = { inner: [] };
state.meHands = { inner: [] };
state.opHands = { inner: [] };
for (const reload of action.payload.actions) {
const player = reload.player;
// DECK
const deck = judgeSelf(player, state) ? state.meDeck : state.opDeck;
reloadFieldMeta(
deck,
reload.zone_actions.filter((item) => item.zone == ygopro.CardZone.DECK),
player
);
// EXTRA_DECK
const extraDeck = judgeSelf(player, state)
? state.meExtraDeck
: state.opExtraDeck;
reloadFieldMeta(
extraDeck,
reload.zone_actions.filter((item) => item.zone == ygopro.CardZone.EXTRA),
player
);
// MZONE
const monster = judgeSelf(player, state)
? state.meMonsters
: state.opMonsters;
reloadFieldMeta(
monster,
reload.zone_actions.filter((item) => item.zone == ygopro.CardZone.MZONE),
player
);
// SZONE
const magics = judgeSelf(player, state) ? state.meMagics : state.opMagics;
reloadFieldMeta(
magics,
reload.zone_actions.filter((item) => item.zone == ygopro.CardZone.SZONE),
player
);
// GRAVE
const graveyard = judgeSelf(player, state)
? state.meGraveyard
: state.opGraveyard;
reloadFieldMeta(
graveyard,
reload.zone_actions.filter((item) => item.zone == ygopro.CardZone.GRAVE),
player
);
// REMOVED
const banishedZone = judgeSelf(player, state)
? state.meBanishedZone
: state.opBanishedZone;
reloadFieldMeta(
banishedZone,
reload.zone_actions.filter(
(item) => item.zone == ygopro.CardZone.REMOVED
),
player
);
// HANDS
const hands = judgeSelf(player, state) ? state.meHands : state.opHands;
reloadFieldMeta(
hands,
reload.zone_actions.filter((item) => item.zone == ygopro.CardZone.HAND),
player
);
}
};
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { RootState } from "@/store";
import { CardState, DuelFieldState } from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface DeckState extends DuelFieldState {}
// 初始化卡组状态
export const initDeckImpl: CaseReducer<
DuelState,
PayloadAction<{ player: number; deskSize: number }>
> = (state, action) => {
const player = action.payload.player;
const deckSize = action.payload.deskSize;
let deck: CardState[] = new Array(deckSize);
for (let i = 0; i < deckSize; i++) {
deck.push({
occupant: { id: 0, data: {}, text: {} },
location: {
controler: player,
location: ygopro.CardZone.DECK,
},
idleInteractivities: [],
counters: {},
});
}
if (judgeSelf(player, state)) {
state.meDeck = { inner: deck };
} else {
state.opDeck = { inner: deck };
}
};
export const selectMeDeck = (state: RootState) =>
state.duel.meDeck || { inner: [] };
export const selectOpDeck = (state: RootState) =>
state.duel.opDeck || { inner: [] };
import { ActionReducerMapBuilder } from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { RootState } from "@/store";
import {
createAsyncMetaThunk,
createAsyncRepeatedMetaThunk,
DuelFieldState,
DuelReducer,
extendIdleInteractivities,
extendMeta,
extendState,
Interactivity,
removeCard,
updateCardMeta,
} from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface ExtraDeckState extends DuelFieldState {}
// 初始化额外卡组
export const initMeExtraDeckMeta = createAsyncRepeatedMetaThunk(
"duel/initExtraDeckMeta"
);
// 增加额外卡组
export const fetchExtraDeckMeta = createAsyncMetaThunk(
"duel/fetchExtraDeckMeta"
);
export const extraDeckCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(initMeExtraDeckMeta.pending, (state, action) => {
const _ = action.meta.arg.controler;
const ids = action.meta.arg.codes;
const cards = ids.map((id) => {
return {
occupant: { id, data: {}, text: {} },
location: {
location: ygopro.CardZone.EXTRA,
},
idleInteractivities: [],
counters: {},
};
});
state.meExtraDeck = { inner: cards };
});
builder.addCase(initMeExtraDeckMeta.fulfilled, (state, action) => {
const _ = action.payload.controler;
const metas = action.payload.metas;
updateCardMeta(state.meExtraDeck, metas);
});
builder.addCase(fetchExtraDeckMeta.pending, (state, action) => {
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
const code = action.meta.arg.code;
const newExtraDeck = {
occupant: { id: code, data: {}, text: {} },
location: {
controler,
location: ygopro.CardZone.EXTRA,
},
idleInteractivities: [],
counters: {},
};
const extraDeck = judgeSelf(controler, state)
? state.meExtraDeck
: state.opExtraDeck;
extendState(extraDeck, newExtraDeck, sequence);
});
builder.addCase(fetchExtraDeckMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const meta = action.payload.meta;
const extraDeck = judgeSelf(controler, state)
? state.meExtraDeck
: state.opExtraDeck;
extendMeta(extraDeck, meta, sequence);
});
};
// 删除额外卡组
export const removeExtraDeckImpl: DuelReducer<{
controler: number;
sequence: number;
}> = (state, action) => {
const extraDeck = judgeSelf(action.payload.controler, state)
? state.meExtraDeck
: state.opExtraDeck;
removeCard(extraDeck, action.payload.sequence);
};
export const addExtraDeckIdleInteractivitiesImpl: DuelReducer<{
player: number;
sequence: number;
interactivity: Interactivity<number>;
}> = (state, action) => {
const extraDeck = judgeSelf(action.payload.player, state)
? state.meExtraDeck
: state.opExtraDeck;
extendIdleInteractivities(
extraDeck,
action.payload.sequence,
action.payload.interactivity
);
};
export const selectMeExtraDeck = (state: RootState) =>
state.duel.meExtraDeck || { inner: [] };
export const selectOpExtraDeck = (state: RootState) =>
state.duel.opExtraDeck || { inner: [] };
import {
AsyncThunk,
CaseReducer,
createAsyncThunk,
PayloadAction,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { CardMeta, fetchCard } from "@/api/cards";
import { DuelState } from "./mod";
import ReloadFieldAction = ygopro.StocGameMessage.MsgReloadField.ZoneAction;
type UpdateDataAction = ReturnType<
typeof ygopro.StocGameMessage.MsgUpdateData.Action.prototype.toObject
>;
export type DuelReducer<T> = CaseReducer<DuelState, PayloadAction<T>>;
export interface DuelFieldState {
inner: CardState[];
}
export interface CardState {
occupant?: CardMeta; // 占据此位置的卡牌元信息
location: {
controler?: number;
location?: number;
position?: ygopro.CardPosition;
overlay_sequence?: number;
}; // 位置信息
idleInteractivities: Interactivity<number>[]; // IDLE状态下的互动信息
placeInteractivities?: Interactivity<{
controler: number;
zone: ygopro.CardZone;
sequence: number;
}>; // 选择位置状态下的互动信息
overlay_materials?: CardMeta[]; // 超量素材
counters: { [type: number]: number }; // 指示器
reload?: boolean; // 这个字段会在收到MSG_RELOAD_FIELD的时候设置成true,在收到MSG_UPDATE_DATE的时候设置成false
}
export enum InteractType {
// 可普通召唤
SUMMON = 1,
// 可特殊召唤
SP_SUMMON = 2,
// 可改变表示形式
POS_CHANGE = 3,
// 可前场放置
MSET = 4,
// 可后场放置
SSET = 5,
// 可发动效果
ACTIVATE = 6,
// 可作为位置选择
PLACE_SELECTABLE = 7,
// 可攻击
ATTACK = 8,
}
export interface Interactivity<T> {
interactType: InteractType;
// 如果`interactType`是`ACTIVATE`,这个字段是对应的效果编号
activateIndex?: number;
// 如果`interactType`是`ATTACK`,这个字段表示是否可以直接攻击
directAttackAble?: boolean;
// 用户点击后,需要回传给服务端的`response`
response: T;
}
export function createAsyncMetaThunk(name: string): AsyncThunk<
{ controler: number; sequence: number; meta: CardMeta },
{
controler: number;
sequence: number;
position?: ygopro.CardPosition;
code: number;
},
{}
> {
return createAsyncThunk(
name,
async (param: {
controler: number;
sequence: number;
position?: ygopro.CardPosition;
code: number;
}) => {
const code = param.code;
const meta = await fetchCard(code, true);
const response = {
controler: param.controler,
sequence: param.sequence,
meta,
};
return response;
}
);
}
export function createAsyncRepeatedMetaThunk(
name: string
): AsyncThunk<
{ controler: number; metas: CardMeta[] },
{ controler: number; codes: number[] },
{}
> {
return createAsyncThunk(
name,
async (param: { controler: number; codes: number[] }) => {
const controler = param.controler;
const Ids = param.codes;
const metas = await Promise.all(
Ids.map(async (id) => {
if (id == 0) {
return { id, data: {}, text: {} };
} else {
return await fetchCard(id, true);
}
})
);
const response = { controler, metas };
return response;
}
);
}
/*
* 扩充决斗区域卡片内容
*
* @param state - 需要扩充的区域,比如`MonsterState`
* @param newState - 新增加的`CardState`
* @sequence - 新增加的卡片的序列号,可选,如果为空则补充到列表末尾
*
* */
export function extendState<T extends DuelFieldState>(
state: T | undefined,
newState: CardState,
sequence?: number
) {
if (state) {
let index = sequence !== undefined ? sequence : state.inner.length;
state.inner.splice(index, 0, newState);
}
}
export function extendOccupant<T extends DuelFieldState>(
state: T | undefined,
newMeta: CardMeta,
sequence: number,
position?: ygopro.CardPosition
) {
if (state) {
const target = state.inner.find((_, idx) => idx == sequence);
if (target) {
target.occupant = newMeta;
if (typeof position !== "undefined") {
target.location.position = position;
}
}
}
}
export function extendMeta<T extends DuelFieldState>(
state: T | undefined,
newMeta: CardMeta,
sequence: number
) {
if (state) {
const target = state.inner.find((_, idx) => idx == sequence);
if (target) {
target.occupant = newMeta;
}
}
}
export function extendPlaceInteractivity<T extends DuelFieldState>(
state: T | undefined,
controler: number,
sequence: number,
zone: ygopro.CardZone
) {
if (state) {
const target = state.inner.find((_, idx) => idx == sequence);
if (target) {
target.placeInteractivities = {
interactType: InteractType.PLACE_SELECTABLE,
response: {
controler,
zone,
sequence,
},
};
}
}
}
export function clearPlaceInteractivities<T extends DuelFieldState>(
state: T | undefined
) {
if (state) {
for (let item of state.inner) {
item.placeInteractivities = undefined;
}
}
}
export function removeCard<T extends DuelFieldState>(
state: T | undefined,
sequence: number
) {
if (state) {
state.inner = state.inner.filter((_, idx) => idx != sequence);
}
}
export function removeOccupant<T extends DuelFieldState>(
state: T | undefined,
sequence: number
) {
if (state) {
const target = state.inner.find((_, idx) => idx == sequence);
if (target) {
target.occupant = undefined;
}
}
}
export function removeOverlay<T extends DuelFieldState>(
state: T | undefined,
sequence: number
) {
if (state) {
const target = state.inner.find((_, idx) => idx == sequence);
if (target) {
target.overlay_materials = [];
}
}
}
export function insertCard<T extends DuelFieldState>(
state: T | undefined,
sequence: number,
card: CardState
) {
if (state) {
state.inner.splice(sequence, 0, card);
}
}
export function updateCardMeta<T extends DuelFieldState>(
state: T | undefined,
metas: CardMeta[]
) {
if (state) {
state.inner.forEach((item) => {
metas.forEach((meta) => {
if (item.occupant?.id === meta.id) {
item.occupant = meta;
}
});
});
}
}
export function extendIdleInteractivities<T extends DuelFieldState>(
state: T | undefined,
sequence: number,
interactivity: Interactivity<number>
) {
if (state) {
const target = state.inner.find((_, idx) => idx == sequence);
if (target) {
target.idleInteractivities.push(interactivity);
}
}
}
export function clearIdleInteractivities<T extends DuelFieldState>(
state: T | undefined
) {
if (state) {
state.inner.forEach((item) => {
item.idleInteractivities = [];
});
}
}
export function setPosition<T extends DuelFieldState>(
state: T | undefined,
sequence: number,
position: ygopro.CardPosition
) {
const target = state?.inner.find((_, idx) => idx == sequence);
if (target && target.occupant) {
target.location.position = position;
}
}
export function updateCardData<T extends DuelFieldState>(
state: T | undefined,
actions: UpdateDataAction[]
) {
for (const payload of actions) {
const sequence = payload.location?.sequence;
if (typeof sequence !== "undefined") {
const target = state?.inner.find((_, idx) => idx == sequence);
if (target && (target.occupant || target.reload)) {
if (target.occupant === undefined) {
target.occupant = { id: payload.code!, data: {}, text: {} };
}
const occupant = target.occupant;
// 目前只更新以下字段
if (payload.code !== undefined && payload.code >= 0) {
occupant.id = payload.code;
occupant.text.id = payload.code;
}
if (payload.location !== undefined) {
target.location.position = payload.location.position;
}
if (payload.type_ !== undefined && payload.type_ >= 0) {
occupant.data.type = payload.type_;
}
if (payload.level !== undefined && payload.level >= 0) {
occupant.data.level = payload.level;
}
if (payload.attribute !== undefined && payload.attribute >= 0) {
occupant.data.attribute = payload.attribute;
}
if (payload.race !== undefined && payload.race >= 0) {
occupant.data.race = payload.race;
}
if (payload.attack !== undefined && payload.attack >= 0) {
occupant.data.atk = payload.attack;
}
if (payload.defense !== undefined && payload.defense >= 0) {
occupant.data.def = payload.defense;
}
// TODO: counters
}
if (target?.reload) {
target.reload = false;
}
}
}
}
export function reloadFieldMeta<T extends DuelFieldState>(
state: T,
actions: ReloadFieldAction[],
controler: number
) {
actions.sort((a, b) => a.sequence - b.sequence);
const cards = actions.map((action) => {
// FIXME: OVERLAY
return {
location: {
controler,
location: action.zone,
position: action.position,
},
idleInteractivities: [],
counters: {},
reload: true,
};
});
state.inner = cards;
}
import {
ActionReducerMapBuilder,
CaseReducer,
PayloadAction,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { RootState } from "@/store";
import {
createAsyncMetaThunk,
DuelFieldState,
DuelReducer,
extendIdleInteractivities,
extendMeta,
extendState,
Interactivity,
removeCard,
} from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface GraveyardState extends DuelFieldState {}
// 初始化墓地状态
export const initGraveyardImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
const player = action.payload;
if (judgeSelf(player, state)) {
state.meGraveyard = { inner: [] };
} else {
state.opGraveyard = { inner: [] };
}
};
// 增加墓地
export const fetchGraveyardMeta = createAsyncMetaThunk(
"duel/fetchGraveyardMeta"
);
export const graveyardCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchGraveyardMeta.pending, (state, action) => {
// Meta结果没返回之前先更新`ID`
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
const code = action.meta.arg.code;
const newGraveyard = {
occupant: { id: code, data: {}, text: {} },
location: {
controler,
location: ygopro.CardZone.GRAVE,
},
idleInteractivities: [],
counters: {},
};
if (judgeSelf(controler, state)) {
extendState(state.meGraveyard, newGraveyard, sequence);
} else {
extendState(state.opGraveyard, newGraveyard, sequence);
}
});
builder.addCase(fetchGraveyardMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const meta = action.payload.meta;
if (judgeSelf(controler, state)) {
extendMeta(state.meGraveyard, meta, sequence);
} else {
extendMeta(state.opGraveyard, meta, sequence);
}
});
};
// 删除墓地
export const removeGraveyardImpl: CaseReducer<
DuelState,
PayloadAction<{ controler: number; sequence: number }>
> = (state, action) => {
const graveyard = judgeSelf(action.payload.controler, state)
? state.meGraveyard
: state.opGraveyard;
removeCard(graveyard, action.payload.sequence);
};
export const addGraveyardIdleInteractivitiesImpl: DuelReducer<{
player: number;
sequence: number;
interactivity: Interactivity<number>;
}> = (state, action) => {
const graveyard = judgeSelf(action.payload.player, state)
? state.meGraveyard
: state.opGraveyard;
extendIdleInteractivities(
graveyard,
action.payload.sequence,
action.payload.interactivity
);
};
export const selectMeGraveyard = (state: RootState) =>
state.duel.meGraveyard || { inner: [] };
export const selectOpGraveyard = (state: RootState) =>
state.duel.opGraveyard || { inner: [] };
import {
ActionReducerMapBuilder,
CaseReducer,
PayloadAction,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { RootState } from "@/store";
import {
createAsyncMetaThunk,
createAsyncRepeatedMetaThunk,
DuelFieldState,
extendMeta,
insertCard,
Interactivity,
removeCard,
updateCardMeta,
} from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface HandState extends DuelFieldState {}
// 增加手牌
export const fetchHandsMeta = createAsyncRepeatedMetaThunk(
"duel/fetchHandsMeta"
);
// 清空手牌互动性
export const clearHandsIdleInteractivityImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
const player = action.payload;
const hands = judgeSelf(player, state) ? state.meHands : state.opHands;
if (hands) {
for (let hand of hands.inner) {
hand.idleInteractivities = [];
}
}
};
// 添加手牌互动性
export const addHandsIdleInteractivityImpl: CaseReducer<
DuelState,
PayloadAction<{
player: number;
sequence: number;
interactivity: Interactivity<number>;
}>
> = (state, action) => {
const player = action.payload.player;
const hands = judgeSelf(player, state) ? state.meHands : state.opHands;
if (hands) {
const sequence = action.payload.sequence;
const interactivity = action.payload.interactivity;
hands.inner[sequence].idleInteractivities.push(interactivity);
}
};
// 删除手牌
export const removeHandImpl: CaseReducer<
DuelState,
PayloadAction<[number, number]>
> = (state, action) => {
const controler = action.payload[0];
const sequence = action.payload[1];
const hands = judgeSelf(controler, state) ? state.meHands : state.opHands;
removeCard(hands, sequence);
};
// 在特定位置增加手牌
export const insertHandMeta = createAsyncMetaThunk("duel/insertHandMeta");
export const updateHandsMeta = createAsyncRepeatedMetaThunk(
"duel/updateHandsMeta"
);
export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchHandsMeta.pending, (state, action) => {
// Meta结果没返回之前先更新手牌`ID`
const player = action.meta.arg.controler;
const ids = action.meta.arg.codes;
const cards = ids.map((id) => {
return {
occupant: { id, data: {}, text: {} },
location: {
controler: player,
location: ygopro.CardZone.HAND,
},
counters: {},
idleInteractivities: [],
};
});
if (judgeSelf(player, state)) {
if (state.meHands) {
state.meHands.inner = state.meHands.inner.concat(cards);
} else {
state.meHands = { inner: cards };
}
} else {
if (state.opHands) {
state.opHands.inner = state.opHands.inner.concat(cards);
} else {
state.opHands = { inner: cards };
}
}
});
builder.addCase(fetchHandsMeta.fulfilled, (state, action) => {
// `Meta`结果回来后更新手牌的`Meta`结果
const player = action.payload.controler;
const metas = action.payload.metas;
const hands = judgeSelf(player, state) ? state.meHands : state.opHands;
updateCardMeta(hands, metas);
});
builder.addCase(insertHandMeta.pending, (state, action) => {
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
const code = action.meta.arg.code;
const hands = judgeSelf(controler, state) ? state.meHands : state.opHands;
insertCard(hands, sequence, {
occupant: { id: code, data: {}, text: {} },
location: { controler },
idleInteractivities: [],
counters: {},
});
});
builder.addCase(insertHandMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const meta = action.payload.meta;
const hands = judgeSelf(controler, state) ? state.meHands : state.opHands;
extendMeta(hands, meta, sequence);
});
builder.addCase(updateHandsMeta.pending, (state, action) => {
const controler = action.meta.arg.controler;
const codes = action.meta.arg.codes;
const metas = codes.map((code) => {
return {
occupant: { id: code, data: {}, text: {} },
location: {
controler,
location: ygopro.CardZone.HAND,
},
idleInteractivities: [],
counters: {},
};
});
const hands = judgeSelf(controler, state) ? state.meHands : state.opHands;
if (hands) {
hands.inner = metas;
}
});
builder.addCase(updateHandsMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const metas = action.payload.metas;
const hands = judgeSelf(controler, state) ? state.meHands : state.opHands;
updateCardMeta(hands, metas);
});
};
export const selectMeHands = (state: RootState) =>
state.duel.meHands || { inner: [] };
export const selectOpHands = (state: RootState) =>
state.duel.opHands || { inner: [] };
import { ActionReducerMapBuilder, createAsyncThunk } from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { fetchCard } from "@/api/cards";
import { DESCRIPTION_LIMIT, fetchStrings, getStrings } from "@/api/strings";
import { RootState } from "@/store";
import { DuelReducer } from "./generic";
import { DuelState } from "./mod";
import { findCardByLocation } from "./util";
export interface HintState {
code: number;
msg?: string;
esHint?: string;
esSelectHint?: string;
}
export const initHintImpl: DuelReducer<void> = (state) => {
state.hint = { code: 0 };
};
export const fetchCommonHintMeta = createAsyncThunk(
"duel/fetchCommonHintMeta",
async (hintData: number) => {
return fetchStrings("!system", hintData);
}
);
export const fetchSelectHintMeta = createAsyncThunk(
"duel/fetchSelectHintMeta",
async (param: { selectHintData: number; esHint?: string }) => {
const selectHintData = param.selectHintData;
let selectHintMeta = "";
if (selectHintData > DESCRIPTION_LIMIT) {
// 针对`MSG_SELECT_PLACE`的特化逻辑
const cardMeta = await fetchCard(selectHintData, true);
selectHintMeta = fetchStrings("!system", 569).replace(
"[%ls]",
cardMeta.text.name || "[?]"
);
} else {
selectHintMeta = await getStrings(selectHintData);
}
return {
selectHintMeta,
esHint: param.esHint,
};
}
);
export const fetchEsHintMeta = createAsyncThunk(
"duel/fetchEsHintMeta",
async (param: {
originMsg: string | number;
location?: ygopro.CardLocation;
cardID?: number;
}) => {
const originMsg =
typeof param.originMsg === "string"
? param.originMsg
: fetchStrings("!system", param.originMsg);
const location = param.location;
if (param.cardID) {
const cardMeta = await fetchCard(param.cardID, true);
return { originMsg, cardMeta, location };
} else {
return { originMsg, location };
}
}
);
export const hintCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchCommonHintMeta.pending, (state, action) => {
const code = action.meta.arg;
if (state.hint) {
state.hint.code = code;
}
});
builder.addCase(fetchCommonHintMeta.fulfilled, (state, action) => {
const hintMeta = action.payload;
if (state.hint) {
state.hint.msg = hintMeta;
}
});
builder.addCase(fetchSelectHintMeta.pending, (state, action) => {
const code = action.meta.arg.selectHintData;
if (state.hint) {
state.hint.code = code;
}
});
builder.addCase(fetchSelectHintMeta.fulfilled, (state, action) => {
const selectHintMsg = action.payload.selectHintMeta;
const esHint = action.payload.esHint;
const hint = state.hint;
if (hint) {
if (hint.code > DESCRIPTION_LIMIT) {
// 针对`MSG_SELECT_PLACE`的特化逻辑
hint.msg = selectHintMsg;
} else {
hint.esSelectHint = selectHintMsg;
if (esHint) hint.esHint = esHint;
}
}
});
builder.addCase(fetchEsHintMeta.fulfilled, (state, action) => {
const originMsg = action.payload.originMsg;
const cardMeta = action.payload.cardMeta;
const location = action.payload.location;
const hint = state.hint;
if (hint) {
let esHint = originMsg;
if (cardMeta?.text.name) {
esHint = originMsg.replace("[?]", cardMeta.text.name);
}
if (location) {
const fieldMeta = findCardByLocation(state, location);
if (fieldMeta?.occupant?.text.name) {
esHint = originMsg.replace("[?]", fieldMeta.occupant.text.name);
}
}
hint.esHint = esHint;
}
});
};
export const selectHint = (state: RootState) => state.duel.hint;
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { RootState } from "@/store";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
import MsgUpdateHp = ygopro.StocGameMessage.MsgUpdateHp;
export interface InitInfo {
masterRule?: string;
life: number;
deckSize: number;
extraSize: number;
}
// 更新自己的初始生命值,卡组信息
export const infoInitImpl: CaseReducer<
DuelState,
PayloadAction<[number, InitInfo]>
> = (state, action) => {
const player = action.payload[0];
const initInfo = action.payload[1];
if (judgeSelf(player, state)) {
state.meInitInfo = initInfo;
} else {
state.opInitInfo = initInfo;
}
};
export const updateHpImpl: CaseReducer<
DuelState,
PayloadAction<ygopro.StocGameMessage.MsgUpdateHp>
> = (state, action) => {
const player = action.payload.player;
const actionType = action.payload.type_;
const value = action.payload.value;
const info = judgeSelf(player, state) ? state.meInitInfo : state.opInitInfo;
if (info) {
switch (actionType) {
case MsgUpdateHp.ActionType.DAMAGE: {
info.life = info.life - value;
break;
}
case MsgUpdateHp.ActionType.RECOVER: {
info.life = info.life + value;
break;
}
default: {
break;
}
}
}
};
export const selectMeInitInfo = (state: RootState) => state.duel.meInitInfo;
export const selectOpInitInfo = (state: RootState) => state.duel.opInitInfo;
import {
ActionReducerMapBuilder,
CaseReducer,
PayloadAction,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { RootState } from "@/store";
import {
clearIdleInteractivities,
clearPlaceInteractivities,
createAsyncMetaThunk,
DuelFieldState,
extendIdleInteractivities,
extendOccupant,
extendPlaceInteractivity,
Interactivity,
removeOccupant,
setPosition,
} from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface MagicState extends DuelFieldState {}
// 初始化自己的魔法陷阱区状态
export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = (
state,
action
) => {
const player = action.payload;
const magics = {
inner: [
{
location: {
controler: player,
location: ygopro.CardZone.SZONE,
},
idleInteractivities: [],
counters: {},
},
{
location: {
controler: player,
location: ygopro.CardZone.SZONE,
},
idleInteractivities: [],
counters: {},
},
{
location: {
controler: player,
location: ygopro.CardZone.SZONE,
},
idleInteractivities: [],
counters: {},
},
{
location: {
controler: player,
location: ygopro.CardZone.SZONE,
},
idleInteractivities: [],
counters: {},
},
{
location: {
controler: player,
location: ygopro.CardZone.SZONE,
},
idleInteractivities: [],
counters: {},
},
{
// 场地区
location: {
controler: player,
location: ygopro.CardZone.SZONE,
},
idleInteractivities: [],
counters: {},
},
],
};
if (judgeSelf(player, state)) {
state.meMagics = magics;
} else {
state.opMagics = magics;
}
};
export const addMagicPlaceInteractivitiesImpl: CaseReducer<
DuelState,
PayloadAction<[number, number]>
> = (state, action) => {
const controler = action.payload[0];
const sequence = action.payload[1];
const magics = judgeSelf(controler, state) ? state.meMagics : state.opMagics;
extendPlaceInteractivity(magics, controler, sequence, ygopro.CardZone.SZONE);
};
export const clearMagicPlaceInteractivitiesImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
const player = action.payload;
const magics = judgeSelf(player, state) ? state.meMagics : state.opMagics;
clearPlaceInteractivities(magics);
};
export const addMagicIdleInteractivitiesImpl: CaseReducer<
DuelState,
PayloadAction<{
player: number;
sequence: number;
interactivity: Interactivity<number>;
}>
> = (state, action) => {
const magics = judgeSelf(action.payload.player, state)
? state.meMagics
: state.opMagics;
extendIdleInteractivities(
magics,
action.payload.sequence,
action.payload.interactivity
);
};
export const clearMagicIdleInteractivitiesImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
const magics = judgeSelf(action.payload, state)
? state.meMagics
: state.opMagics;
clearIdleInteractivities(magics);
};
// 增加魔法陷阱
export const fetchMagicMeta = createAsyncMetaThunk("duel/fetchMagicMeta");
export const magicCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchMagicMeta.pending, (state, action) => {
// Meta结果没返回之前先更新`ID`
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
const position = action.meta.arg.position;
const code = action.meta.arg.code;
const meta = { id: code, data: {}, text: {} };
if (judgeSelf(controler, state)) {
extendOccupant(state.meMagics, meta, sequence, position);
} else {
extendOccupant(state.opMagics, meta, sequence, position);
}
});
builder.addCase(fetchMagicMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const meta = action.payload.meta;
if (judgeSelf(controler, state)) {
extendOccupant(state.meMagics, meta, sequence);
} else {
extendOccupant(state.opMagics, meta, sequence);
}
});
};
// 删除魔法陷阱
export const removeMagicImpl: CaseReducer<
DuelState,
PayloadAction<{ controler: number; sequence: number }>
> = (state, action) => {
const controler = action.payload.controler;
const magics = judgeSelf(controler, state) ? state.meMagics : state.opMagics;
removeOccupant(magics, action.payload.sequence);
};
// 改变魔法表示形式
export const setMagicPositionImpl: CaseReducer<
DuelState,
PayloadAction<{
controler: number;
sequence: number;
position: ygopro.CardPosition;
}>
> = (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const position = action.payload.position;
const magics = judgeSelf(controler, state) ? state.meMagics : state.opMagics;
setPosition(magics, sequence, position);
};
export const selectMeMagics = (state: RootState) =>
state.duel.meMagics || { inner: [] };
export const selectOpMagics = (state: RootState) =>
state.duel.opMagics || { inner: [] };
/*
* 对局内的状态更新逻辑
*
* */
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { RootState } from "@/store";
import {
addBanishedZoneIdleInteractivitiesImpl,
banishedZoneCase,
BanishedZoneState,
initBanishedZoneImpl,
removeBanishedZoneImpl,
} from "./banishedZoneSlice";
import {
clearAllIdleInteractivitiesImpl,
clearAllPlaceInteractivitiesImpl,
reloadFieldImpl,
updateFieldDataImpl,
} from "./commonSlice";
import { DeckState, initDeckImpl } from "./deckSlice";
import {
addExtraDeckIdleInteractivitiesImpl,
extraDeckCase,
ExtraDeckState,
removeExtraDeckImpl,
} from "./extraDeckSlice";
import {
addGraveyardIdleInteractivitiesImpl,
graveyardCase,
GraveyardState,
initGraveyardImpl,
removeGraveyardImpl,
} from "./graveyardSlice";
import {
addHandsIdleInteractivityImpl,
clearHandsIdleInteractivityImpl,
handsCase,
HandState,
removeHandImpl,
} from "./handsSlice";
import { hintCase, HintState, initHintImpl } from "./hintSlice";
import { infoInitImpl, InitInfo, updateHpImpl } from "./initInfoSlice";
import {
addMagicIdleInteractivitiesImpl,
addMagicPlaceInteractivitiesImpl,
clearMagicIdleInteractivitiesImpl,
clearMagicPlaceInteractivitiesImpl,
initMagicsImpl,
magicCase,
MagicState,
removeMagicImpl,
setMagicPositionImpl,
} from "./magicSlice";
import {
checkCardModalCase,
checkCardModalV2Case,
checkCardModalV3Case,
clearCheckCounterImpl,
ModalState,
optionModalCase,
resetCheckCardModalImpl,
resetCheckCardModalV2Impl,
resetCheckCardModalV3Impl,
resetOptionModalImpl,
resetPositionModalImpl,
resetSortCardModalImpl,
setCardListModalInfoImpl,
setCardListModalIsOpenImpl,
setCardModalCountersImpl,
setCardModalInteractiviesImpl,
setCardModalIsOpenImpl,
setCardModalMetaImpl,
setCheckCardMOdalCancelAbleImpl,
setCheckCardModalCancelResponseImpl,
setCheckCardModalIsOpenImpl,
setCheckCardModalMinMaxImpl,
setCheckCardModalOnSubmitImpl,
setCheckCardModalV2CancelAbleImpl,
setCheckCardModalV2FinishAbleImpl,
setCheckCardModalV2IsOpenImpl,
setCheckCardModalV2MinMaxImpl,
setCheckCardModalV2ResponseAbleImpl,
setCheckCardModalV3AllLevelImpl,
setCheckCardModalV3IsOpenImpl,
setCheckCardModalV3MinMaxImpl,
setCheckCardModalV3OverFlowImpl,
setCheckCardModalV3ResponseAbleImpl,
setCheckCounterImpl,
setOptionModalIsOpenImpl,
setPositionModalIsOpenImpl,
setPositionModalPositionsImpl,
setSortCardModalIsOpenImpl,
setYesNoModalIsOpenImpl,
sortCardModalCase,
YesNoModalCase,
} from "./modal/mod";
import {
addMonsterIdleInteractivitiesImpl,
addMonsterPlaceInteractivitiesImpl,
clearMonsterIdleInteractivitiesImpl,
clearMonsterPlaceInteractivitiesImpl,
initMonstersImpl,
monsterCase,
MonsterState,
removeMonsterImpl,
removeOverlayImpl,
setMonsterPositionImpl,
updateMonsterCountersImpl,
} from "./monstersSlice";
import {
newPhaseImpl,
PhaseState,
setEnableBpImpl,
setEnableEpImpl,
setEnableM2Impl,
} from "./phaseSlice";
import { TimeLimit, updateTimeLimitImpl } from "./timeLimit";
import { newTurnImpl } from "./turnSlice";
import MsgWin = ygopro.StocGameMessage.MsgWin;
export interface DuelState {
selfType?: number;
meInitInfo?: InitInfo; // 自己的初始状态
opInitInfo?: InitInfo; // 对手的初始状态
meHands?: HandState; // 自己的手牌
opHands?: HandState; // 对手的手牌
meMonsters?: MonsterState; // 自己的怪兽区状态
opMonsters?: MonsterState; // 对手的怪兽区状态
meMagics?: MagicState; // 自己的魔法陷阱区状态
opMagics?: MagicState; // 对手的魔法陷阱区状态
meGraveyard?: GraveyardState; // 自己的墓地状态
opGraveyard?: GraveyardState; // 对手的墓地状态
meBanishedZone?: BanishedZoneState; // 自己的除外区状态
opBanishedZone?: BanishedZoneState; // 对手的除外区状态
meDeck?: DeckState; // 自己的卡组状态
opDeck?: DeckState; // 对手的卡组状态
meExtraDeck?: ExtraDeckState; // 自己的额外卡组状态
opExtraDeck?: ExtraDeckState; // 对手的额外卡组状态
meTimeLimit?: TimeLimit; // 自己的计时
opTimeLimit?: TimeLimit; // 对手的计时
hint?: HintState;
currentPlayer?: number; // 当前的操作方
phase?: PhaseState;
result?: MsgWin.ActionType;
waiting?: boolean;
unimplemented?: number; // 未处理的`Message`
// UI相关
modalState: ModalState;
}
const initialState: DuelState = {
modalState: {
cardModal: { isOpen: false, interactivies: [], counters: {} },
cardListModal: { isOpen: false, list: [] },
checkCardModal: { isOpen: false, cancelAble: false, tags: [] },
yesNoModal: { isOpen: false },
positionModal: { isOpen: false, positions: [] },
optionModal: { isOpen: false, options: [] },
checkCardModalV2: {
isOpen: false,
cancelAble: false,
finishAble: false,
responseable: false,
selectableOptions: [],
selectedOptions: [],
},
checkCardModalV3: {
isOpen: false,
overflow: false,
allLevel: 0,
mustSelectList: [],
selectAbleList: [],
},
checkCounterModal: {
isOpen: false,
options: [],
},
sortCardModal: {
isOpen: false,
options: [],
},
},
};
const duelSlice = createSlice({
name: "duel",
initialState,
reducers: {
setSelfType: (state, action: PayloadAction<number>) => {
state.selfType = action.payload;
},
infoInit: infoInitImpl,
updateHp: updateHpImpl,
updateTurn: newTurnImpl,
updateTimeLimit: updateTimeLimitImpl,
// 手牌相关`Reducer`
clearHandsIdleInteractivity: clearHandsIdleInteractivityImpl,
addHandsIdleInteractivity: addHandsIdleInteractivityImpl,
removeHand: removeHandImpl,
// 怪兽区相关`Reducer`
initMonsters: initMonstersImpl,
addMonsterPlaceInteractivities: addMonsterPlaceInteractivitiesImpl,
clearMonsterPlaceInteractivities: clearMonsterPlaceInteractivitiesImpl,
addMonsterIdleInteractivities: addMonsterIdleInteractivitiesImpl,
clearMonsterIdleInteractivities: clearMonsterIdleInteractivitiesImpl,
setMonsterPosition: setMonsterPositionImpl,
removeMonster: removeMonsterImpl,
removeOverlay: removeOverlayImpl,
updateMonsterCounters: updateMonsterCountersImpl,
// 魔法陷阱区相关`Reducer`
initMagics: initMagicsImpl,
addMagicPlaceInteractivities: addMagicPlaceInteractivitiesImpl,
clearMagicPlaceInteractivities: clearMagicPlaceInteractivitiesImpl,
addMagicIdleInteractivities: addMagicIdleInteractivitiesImpl,
clearMagicIdleInteractivities: clearMagicIdleInteractivitiesImpl,
setMagicPosition: setMagicPositionImpl,
removeMagic: removeMagicImpl,
// 墓地相关`Reducer`
initGraveyard: initGraveyardImpl,
removeGraveyard: removeGraveyardImpl,
addGraveyardIdleInteractivities: addGraveyardIdleInteractivitiesImpl,
// 除外区相关`Reducer`
initBanishedZone: initBanishedZoneImpl,
removeBanishedZone: removeBanishedZoneImpl,
addBanishedZoneIdleInteractivities: addBanishedZoneIdleInteractivitiesImpl,
// 卡组相关`Reducer`
initDeck: initDeckImpl,
// 额外卡组相关`Reducer`
removeExtraDeck: removeExtraDeckImpl,
addExtraDeckIdleInteractivities: addExtraDeckIdleInteractivitiesImpl,
// 阶段相关
updatePhase: newPhaseImpl,
setEnableBp: setEnableBpImpl,
setEnableM2: setEnableM2Impl,
setEnableEp: setEnableEpImpl,
// UI相关`Reducer`
setCardModalIsOpen: setCardModalIsOpenImpl,
setCardModalMeta: setCardModalMetaImpl,
setCardModalInteractivies: setCardModalInteractiviesImpl,
setCardListModalIsOpen: setCardListModalIsOpenImpl,
setCardListModalInfo: setCardListModalInfoImpl,
setCheckCardModalIsOpen: setCheckCardModalIsOpenImpl,
setCheckCardModalMinMax: setCheckCardModalMinMaxImpl,
setCheckCardModalOnSubmit: setCheckCardModalOnSubmitImpl,
setCheckCardMOdalCancelAble: setCheckCardMOdalCancelAbleImpl,
setCheckCardModalCancelResponse: setCheckCardModalCancelResponseImpl,
resetCheckCardModal: resetCheckCardModalImpl,
setYesNoModalIsOpen: setYesNoModalIsOpenImpl,
setPositionModalIsOpen: setPositionModalIsOpenImpl,
setPositionModalPositions: setPositionModalPositionsImpl,
resetPositionModal: resetPositionModalImpl,
setOptionModalIsOpen: setOptionModalIsOpenImpl,
resetOptionModal: resetOptionModalImpl,
setCheckCardModalV2FinishAble: setCheckCardModalV2FinishAbleImpl,
setCheckCardModalV2MinMax: setCheckCardModalV2MinMaxImpl,
setCheckCardModalV2CancelAble: setCheckCardModalV2CancelAbleImpl,
setCheckCardModalV2IsOpen: setCheckCardModalV2IsOpenImpl,
resetCheckCardModalV2: resetCheckCardModalV2Impl,
setCheckCardModalV2ResponseAble: setCheckCardModalV2ResponseAbleImpl,
setCheckCardModalV3IsOpen: setCheckCardModalV3IsOpenImpl,
setCheckCardModalV3MinMax: setCheckCardModalV3MinMaxImpl,
setCheckCardModalV3AllLevel: setCheckCardModalV3AllLevelImpl,
setCheckCardModalV3OverFlow: setCheckCardModalV3OverFlowImpl,
setCheckCardModalV3ResponseAble: setCheckCardModalV3ResponseAbleImpl,
resetCheckCardModalV3: resetCheckCardModalV3Impl,
setCardModalCounters: setCardModalCountersImpl,
setCheckCounter: setCheckCounterImpl,
clearCheckCounter: clearCheckCounterImpl,
setSortCardModalIsOpen: setSortCardModalIsOpenImpl,
resetSortCardModal: resetSortCardModalImpl,
// 提示相关`Reducer`
initHint: initHintImpl,
// 通用的`Reducer`
clearAllIdleInteractivities: clearAllIdleInteractivitiesImpl,
clearAllPlaceInteractivities: clearAllPlaceInteractivitiesImpl,
updateFieldData: updateFieldDataImpl,
reloadField: reloadFieldImpl,
// 对局结果`Reducer`
setResult: (state, action: PayloadAction<MsgWin.ActionType>) => {
state.result = action.payload;
},
// 等待状态`Reducer`
setWaiting: (state, action: PayloadAction<boolean>) => {
state.waiting = action.payload;
},
// 未处理状态`Reducer`
setUnimplemented: (state, action: PayloadAction<number>) => {
state.unimplemented = action.payload;
},
},
extraReducers(builder) {
handsCase(builder);
hintCase(builder);
monsterCase(builder);
magicCase(builder);
graveyardCase(builder);
banishedZoneCase(builder);
extraDeckCase(builder);
checkCardModalCase(builder);
YesNoModalCase(builder);
optionModalCase(builder);
checkCardModalV2Case(builder);
checkCardModalV3Case(builder);
sortCardModalCase(builder);
},
});
export const {
setSelfType,
infoInit,
updateHp,
updateTurn,
updatePhase,
setEnableBp,
setEnableM2,
setEnableEp,
clearHandsIdleInteractivity,
addHandsIdleInteractivity,
updateTimeLimit,
setCardModalIsOpen,
setCardModalMeta,
setCardModalInteractivies,
initMonsters,
addMonsterPlaceInteractivities,
clearMonsterPlaceInteractivities,
addMonsterIdleInteractivities,
clearMonsterIdleInteractivities,
setMonsterPosition,
removeMonster,
updateMonsterCounters,
removeOverlay,
initMagics,
addMagicPlaceInteractivities,
clearMagicPlaceInteractivities,
addMagicIdleInteractivities,
clearMagicIdleInteractivities,
setMagicPosition,
removeMagic,
removeHand,
initGraveyard,
removeGraveyard,
addGraveyardIdleInteractivities,
setCardListModalIsOpen,
setCardListModalInfo,
setCheckCardModalIsOpen,
setCheckCardModalMinMax,
setCheckCardModalOnSubmit,
setCheckCardMOdalCancelAble,
setCheckCardModalCancelResponse,
resetCheckCardModal,
setYesNoModalIsOpen,
setPositionModalIsOpen,
setPositionModalPositions,
resetPositionModal,
setOptionModalIsOpen,
resetOptionModal,
initDeck,
removeExtraDeck,
addExtraDeckIdleInteractivities,
initBanishedZone,
removeBanishedZone,
addBanishedZoneIdleInteractivities,
setCheckCardModalV2IsOpen,
setCheckCardModalV2MinMax,
setCheckCardModalV2CancelAble,
setCheckCardModalV2FinishAble,
resetCheckCardModalV2,
setCheckCardModalV2ResponseAble,
clearAllIdleInteractivities,
clearAllPlaceInteractivities,
setResult,
setWaiting,
setUnimplemented,
updateFieldData,
reloadField,
setCheckCardModalV3IsOpen,
setCheckCardModalV3MinMax,
setCheckCardModalV3AllLevel,
setCheckCardModalV3OverFlow,
setCheckCardModalV3ResponseAble,
resetCheckCardModalV3,
setCardModalCounters,
setCheckCounter,
clearCheckCounter,
setSortCardModalIsOpen,
resetSortCardModal,
initHint,
} = duelSlice.actions;
export const selectDuelHsStart = (state: RootState) => {
return state.duel.meInitInfo != null;
};
export const selectDuelResult = (state: RootState) => {
return state.duel.result;
};
export const selectWaiting = (state: RootState) => {
return state.duel.waiting;
};
export const selectUnimplemented = (state: RootState) => {
return state.duel.unimplemented;
};
export default duelSlice.reducer;
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { CardMeta } from "@/api/cards";
import { RootState } from "@/store";
import { DuelState } from "../mod";
// 更新卡牌列表弹窗打开状态
export const setCardListModalIsOpenImpl: CaseReducer<
DuelState,
PayloadAction<boolean>
> = (state, action) => {
state.modalState.cardListModal.isOpen = action.payload;
};
// 更新卡牌列表数据
export const setCardListModalInfoImpl: CaseReducer<
DuelState,
PayloadAction<
{
meta?: CardMeta;
interactivies: { desc: string; response: number }[];
}[]
>
> = (state, action) => {
const list = action.payload;
state.modalState.cardListModal.list = list;
};
export const selectCardListModalIsOpen = (state: RootState) =>
state.duel.modalState.cardListModal.isOpen;
export const selectCardListModalInfo = (state: RootState) =>
state.duel.modalState.cardListModal.list;
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { CardMeta } from "@/api/cards";
import { RootState } from "@/store";
import { DuelState } from "../mod";
// 更新卡牌弹窗打开状态
export const setCardModalIsOpenImpl: CaseReducer<
DuelState,
PayloadAction<boolean>
> = (state, action) => {
state.modalState.cardModal.isOpen = action.payload;
};
// 更新卡牌弹窗文本
export const setCardModalMetaImpl: CaseReducer<
DuelState,
PayloadAction<CardMeta>
> = (state, action) => {
state.modalState.cardModal.meta = action.payload;
};
// 更新卡牌弹窗互动选项
export const setCardModalInteractiviesImpl: CaseReducer<
DuelState,
PayloadAction<{ desc: string; response: number }[]>
> = (state, action) => {
state.modalState.cardModal.interactivies = action.payload;
};
// 更新卡牌弹窗指示器
export const setCardModalCountersImpl: CaseReducer<
DuelState,
PayloadAction<{ [type: number]: number }>
> = (state, action) => {
state.modalState.cardModal.counters = action.payload;
};
export const selectCardModalIsOpen = (state: RootState) =>
state.duel.modalState.cardModal.isOpen;
export const selectCardModalMeta = (state: RootState) =>
state.duel.modalState.cardModal.meta;
export const selectCardModalInteractivies = (state: RootState) =>
state.duel.modalState.cardModal.interactivies;
export const selectCardModalCounters = (state: RootState) =>
state.duel.modalState.cardModal.counters;
import {
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
PayloadAction,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { fetchCard, getCardStr } from "@/api/cards";
import { RootState } from "@/store";
import { DuelState } from "../mod";
import { cmpCardLocation, findCardByLocation, judgeSelf } from "../util";
// 更新卡牌选择弹窗打开状态
export const setCheckCardModalIsOpenImpl: CaseReducer<
DuelState,
PayloadAction<boolean>
> = (state, action) => {
state.modalState.checkCardModal.isOpen = action.payload;
};
// 更新卡牌选择弹窗选择数目状态
export const setCheckCardModalMinMaxImpl: CaseReducer<
DuelState,
PayloadAction<{ min: number; max: number }>
> = (state, action) => {
state.modalState.checkCardModal.selectMin = action.payload.min;
state.modalState.checkCardModal.selectMax = action.payload.max;
};
// 更新卡牌选择弹窗的提交回调
export const setCheckCardModalOnSubmitImpl: CaseReducer<
DuelState,
PayloadAction<string>
> = (state, action) => {
state.modalState.checkCardModal.onSubmit = action.payload;
};
// 更新卡牌选择弹窗是否可以取消
export const setCheckCardMOdalCancelAbleImpl: CaseReducer<
DuelState,
PayloadAction<boolean>
> = (state, action) => {
state.modalState.checkCardModal.cancelAble = action.payload;
};
// 更新卡牌选择弹窗取消时返回给服务端的`Response`
export const setCheckCardModalCancelResponseImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
state.modalState.checkCardModal.cancelResponse = action.payload;
};
// 增加卡牌选择选项
export const fetchCheckCardMeta = createAsyncThunk(
"duel/fetchCheckCardMeta",
async (param: {
tagName: string;
option: {
code: number;
location: ygopro.CardLocation;
response: number;
effectDescCode?: number;
};
}) => {
// FIXME: 这里如果传的`controler`如果是对手,对应的`code`会为零,这时候就无法更新对应的`Meta`信息了,后续需要修复。`fetchCheckCardMetaV2`和`fetchCheckCardMetaV3`同理
const meta = await fetchCard(param.option.code, true);
const response = {
tagName: param.tagName,
option: {
meta,
location: param.option.location.toObject(),
},
};
return response;
}
);
export const checkCardModalCase = (
builder: ActionReducerMapBuilder<DuelState>
) => {
builder.addCase(fetchCheckCardMeta.pending, (state, action) => {
const tagName = action.meta.arg.tagName;
const code = action.meta.arg.option.code;
const location = action.meta.arg.option.location;
const controler = location.controler;
const effectDescCode = action.meta.arg.option.effectDescCode;
const response = action.meta.arg.option.response;
const combinedTagName = judgeSelf(controler, state)
? `我方的${tagName}`
: `对方的${tagName}`;
const newID =
code != 0 ? code : findCardByLocation(state, location)?.occupant?.id || 0;
const newOption = {
meta: { id: newID, data: {}, text: {} },
location: location.toObject(),
effectDescCode,
response,
};
for (const tag of state.modalState.checkCardModal.tags) {
if (tag.tagName === combinedTagName) {
tag.options.push(newOption);
return;
}
}
state.modalState.checkCardModal.tags.push({
tagName: combinedTagName,
options: [newOption],
});
});
builder.addCase(fetchCheckCardMeta.fulfilled, (state, action) => {
const tagName = action.payload.tagName;
const option = action.payload.option;
const controler = option.location.controler!;
const combinedTagName = judgeSelf(controler, state)
? `我方的${tagName}`
: `对方的${tagName}`;
for (const tag of state.modalState.checkCardModal.tags) {
if (tag.tagName === combinedTagName) {
for (const old of tag.options) {
if (
option.meta.id == old.meta.id &&
cmpCardLocation(option.location, old.location)
) {
const cardID = old.meta.id;
old.meta = option.meta;
old.meta.id = cardID;
const effectDescCode = old.effectDescCode;
const effectDesc = effectDescCode
? getCardStr(old.meta, effectDescCode & 0xf)
: undefined;
old.effectDesc = effectDesc;
}
}
}
}
});
};
export const resetCheckCardModalImpl: CaseReducer<DuelState> = (state) => {
state.modalState.checkCardModal.isOpen = false;
state.modalState.checkCardModal.selectMin = undefined;
state.modalState.checkCardModal.selectMax = undefined;
state.modalState.checkCardModal.cancelAble = false;
state.modalState.checkCardModal.cancelResponse = undefined;
state.modalState.checkCardModal.tags = [];
};
export const selectCheckCardModalIsOpen = (state: RootState) =>
state.duel.modalState.checkCardModal.isOpen;
export const selectCheckCardModalMinMax = (state: RootState) => {
return {
min: state.duel.modalState.checkCardModal.selectMin || 0,
max: state.duel.modalState.checkCardModal.selectMax || 0,
};
};
export const selectCheckCardModalTags = (state: RootState) =>
state.duel.modalState.checkCardModal.tags;
export const selectCheckCardModalOnSubmit = (state: RootState) =>
state.duel.modalState.checkCardModal.onSubmit;
export const selectCheckCardModalCancelAble = (state: RootState) =>
state.duel.modalState.checkCardModal.cancelAble;
export const selectCheckCardModalCacnelResponse = (state: RootState) =>
state.duel.modalState.checkCardModal.cancelResponse;
import {
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { fetchCard } from "@/api/cards";
import { RootState } from "@/store";
import { DuelReducer } from "../generic";
import { DuelState } from "../mod";
import { findCardByLocation } from "../util";
// 更新打开状态
export const setCheckCardModalV2IsOpenImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV2.isOpen = action.payload;
};
// 更新选择数目
export const setCheckCardModalV2MinMaxImpl: DuelReducer<{
min: number;
max: number;
}> = (state, action) => {
state.modalState.checkCardModalV2.selectMin = action.payload.min;
state.modalState.checkCardModalV2.selectMax = action.payload.max;
};
// 更新是否可以取消
export const setCheckCardModalV2CancelAbleImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV2.cancelAble = action.payload;
};
// 更新是否可以结束
export const setCheckCardModalV2FinishAbleImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV2.finishAble = action.payload;
};
// 更新是否可以回应
export const setCheckCardModalV2ResponseAbleImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV2.responseable = action.payload;
};
// 增加卡牌选项
export const fetchCheckCardMetasV2 = createAsyncThunk(
"duel/fetchCheckCardMetaV2",
async (param: {
selected: boolean;
options: {
code: number;
location: ygopro.CardLocation;
response: number;
}[];
}) => {
const metas = await Promise.all(
param.options.map(async (option) => {
return await fetchCard(option.code, true);
})
);
const response = {
selected: param.selected,
metas,
};
return response;
}
);
export const checkCardModalV2Case = (
builder: ActionReducerMapBuilder<DuelState>
) => {
builder.addCase(fetchCheckCardMetasV2.pending, (state, action) => {
const selected = action.meta.arg.selected;
const options = action.meta.arg.options;
for (const option of options) {
if (option.code == 0) {
const newCode =
findCardByLocation(state, option.location)?.occupant?.id || 0;
option.code = newCode;
}
}
if (selected) {
state.modalState.checkCardModalV2.selectedOptions = options;
} else {
state.modalState.checkCardModalV2.selectableOptions = options;
}
});
builder.addCase(fetchCheckCardMetasV2.fulfilled, (state, action) => {
const selected = action.payload.selected;
const metas = action.payload.metas;
const options = selected
? state.modalState.checkCardModalV2.selectedOptions
: state.modalState.checkCardModalV2.selectableOptions;
options.forEach((option) => {
metas.forEach((meta) => {
if (option.code == meta.id) {
option.name = meta.text.name;
option.desc = meta.text.desc;
}
});
});
});
};
export const resetCheckCardModalV2Impl: CaseReducer<DuelState> = (state) => {
const modalState = state.modalState.checkCardModalV2;
modalState.isOpen = false;
modalState.finishAble = false;
modalState.cancelAble = false;
modalState.responseable = false;
modalState.selectableOptions = [];
modalState.selectedOptions = [];
};
export const selectCheckCardModalV2IsOpen = (state: RootState) =>
state.duel.modalState.checkCardModalV2.isOpen;
export const selectCheckCardModalV2MinMax = (state: RootState) => {
return {
min: state.duel.modalState.checkCardModalV2.selectMin || 0,
max: state.duel.modalState.checkCardModalV2.selectMax || 0,
};
};
export const selectCheckCardModalV2CancelAble = (state: RootState) =>
state.duel.modalState.checkCardModalV2.cancelAble;
export const selectCheckCardModalV2FinishAble = (state: RootState) =>
state.duel.modalState.checkCardModalV2.finishAble;
export const selectCheckCardModalV2ResponseAble = (state: RootState) =>
state.duel.modalState.checkCardModalV2.responseable;
export const selectCheckCardModalV2SelectAbleOptions = (state: RootState) =>
state.duel.modalState.checkCardModalV2.selectableOptions;
export const selectCheckCardModalV2SelectedOptions = (state: RootState) =>
state.duel.modalState.checkCardModalV2.selectedOptions;
import {
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { fetchCard } from "@/api/cards";
import { RootState } from "@/store";
import { DuelReducer } from "../generic";
import { DuelState } from "../mod";
import { findCardByLocation } from "../util";
// 更新打开状态
export const setCheckCardModalV3IsOpenImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV3.isOpen = action.payload;
};
// 更新选择数目
export const setCheckCardModalV3MinMaxImpl: DuelReducer<{
min: number;
max: number;
}> = (state, action) => {
state.modalState.checkCardModalV3.selectMin = action.payload.min;
state.modalState.checkCardModalV3.selectMax = action.payload.max;
};
export const setCheckCardModalV3AllLevelImpl: DuelReducer<number> = (
state,
action
) => {
state.modalState.checkCardModalV3.allLevel = action.payload;
};
export const setCheckCardModalV3OverFlowImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV3.overflow = action.payload;
};
export const setCheckCardModalV3ResponseAbleImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.checkCardModalV3.responseable = action.payload;
};
// 增加卡牌选项
export const fetchCheckCardMetasV3 = createAsyncThunk(
"duel/fetchCheckCardMetaV3",
async (param: {
mustSelect: boolean;
options: {
code: number;
location: ygopro.CardLocation;
level1: number;
level2: number;
response: number;
}[];
}) => {
const metas = await Promise.all(
param.options.map(async (option) => {
return await fetchCard(option.code, true);
})
);
const response = {
mustSelect: param.mustSelect,
metas,
};
return response;
}
);
export const checkCardModalV3Case = (
builder: ActionReducerMapBuilder<DuelState>
) => {
builder.addCase(fetchCheckCardMetasV3.pending, (state, action) => {
const mustSelect = action.meta.arg.mustSelect;
const options = action.meta.arg.options.map((option) => {
if (option.code == 0) {
const newCode =
findCardByLocation(state, option.location)?.occupant?.id || 0;
option.code = newCode;
}
return {
meta: { id: option.code, data: {}, text: {} },
level1: option.level1,
level2: option.level2,
response: option.response,
};
});
if (mustSelect) {
state.modalState.checkCardModalV3.mustSelectList = options;
} else {
state.modalState.checkCardModalV3.selectAbleList = options;
}
});
builder.addCase(fetchCheckCardMetasV3.fulfilled, (state, action) => {
const mustSelect = action.payload.mustSelect;
const metas = action.payload.metas;
const options = mustSelect
? state.modalState.checkCardModalV3.mustSelectList
: state.modalState.checkCardModalV3.selectAbleList;
options.forEach((option) => {
metas.forEach((meta) => {
if (option.meta.id == meta.id) {
option.meta = meta;
}
});
});
});
};
export const resetCheckCardModalV3Impl: CaseReducer<DuelState> = (state) => {
const modalState = state.modalState.checkCardModalV3;
modalState.isOpen = false;
modalState.overflow = false;
modalState.allLevel = 0;
modalState.responseable = undefined;
modalState.mustSelectList = [];
modalState.selectAbleList = [];
};
export const selectCheckCardModalV3 = (state: RootState) =>
state.duel.modalState.checkCardModalV3;
// 后续对于`MSG_SELECT_XXX`的处理UI都尽量用`Babylon.js`实现而不会通过`Antd`的`Modal`实现,因此这里不追求工程质量,暂时简单实现下。
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { RootState } from "@/store";
import { DuelState } from "../mod";
import { findCardByLocation } from "../util";
type SelectCounter = ReturnType<
typeof ygopro.StocGameMessage.MsgSelectCounter.prototype.toObject
>;
export const setCheckCounterImpl: CaseReducer<
DuelState,
PayloadAction<SelectCounter>
> = (state, action) => {
const modal = state.modalState.checkCounterModal;
const payload = action.payload;
modal.counterType = payload.counter_type;
modal.min = payload.min;
modal.options = payload.options!.map((option) => {
const code = option.code
? option.code
: findCardByLocation(state, option.location!)?.occupant?.id || 0;
return {
code,
max: option.counter_count!,
};
});
modal.isOpen = true;
};
export const clearCheckCounterImpl: CaseReducer<DuelState> = (state) => {
state.modalState.checkCounterModal.isOpen = false;
state.modalState.checkCounterModal.min = undefined;
state.modalState.checkCounterModal.counterType = undefined;
state.modalState.checkCounterModal.options = [];
};
export const selectCheckCounterModal = (state: RootState) =>
state.duel.modalState.checkCounterModal;
import { ygopro } from "@/api";
import { CardMeta } from "@/api/cards";
type CardLocation = ReturnType<typeof ygopro.CardLocation.prototype.toObject>;
export interface ModalState {
// 卡牌弹窗
cardModal: {
isOpen: boolean;
meta?: CardMeta;
interactivies: { desc: string; response: number }[];
counters: { [type: number]: number };
};
// 卡牌列表弹窗
cardListModal: {
isOpen: boolean;
list: {
meta?: CardMeta;
interactivies: { desc: string; response: number }[];
}[];
};
// 卡牌选择弹窗
checkCardModal: {
isOpen: boolean;
onSubmit?: string;
selectMin?: number;
selectMax?: number;
cancelAble: boolean;
cancelResponse?: number;
tags: {
tagName: string;
options: {
meta: CardMeta;
location?: CardLocation;
effectDescCode?: number;
effectDesc?: string;
response: number;
}[];
}[];
};
// Yes or No弹窗
yesNoModal: {
isOpen: boolean;
msg?: string;
};
// 表示形式选择弹窗
positionModal: {
isOpen: boolean;
positions: ygopro.CardPosition[];
};
// 选项选择弹窗
optionModal: {
isOpen: boolean;
options: { msg: string; response: number }[];
};
// 卡牌选择弹窗V2
checkCardModalV2: {
isOpen: boolean;
cancelAble: boolean;
finishAble: boolean;
selectMin?: number;
selectMax?: number;
responseable?: boolean;
selectableOptions: {
code: number;
name?: string;
desc?: string;
response: number;
}[];
selectedOptions: {
code: number;
name?: string;
desc?: string;
response: number;
}[];
};
// 卡牌选择弹窗V3
checkCardModalV3: {
isOpen: boolean;
overflow: boolean;
allLevel: number;
selectMin?: number;
selectMax?: number;
responseable?: boolean;
mustSelectList: {
meta: CardMeta;
level1: number;
level2: number;
response: number;
}[];
selectAbleList: {
meta: CardMeta;
level1: number;
level2: number;
response: number;
}[];
};
// 指示器选择弹窗
checkCounterModal: {
isOpen: boolean;
counterType?: number;
min?: number;
options: {
code: number;
max: number;
}[];
};
// 卡牌排序弹窗
sortCardModal: {
isOpen: boolean;
options: {
meta: CardMeta;
response: number;
}[];
};
}
export * from "./cardListModalSlice";
export * from "./cardModalSlice";
export * from "./checkCardModalSlice";
export * from "./checkCardModalV2Slice";
export * from "./checkCardModalV3Slice";
export * from "./checkCounterModalSlice";
export * from "./optionModalSlice";
export * from "./positionModalSlice";
export * from "./sortCardModalSlice";
export * from "./yesNoModalSlice";
import {
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
PayloadAction,
} from "@reduxjs/toolkit";
import { fetchCard, getCardStr } from "@/api/cards";
import { RootState } from "@/store";
import { DuelState } from "../mod";
export const setOptionModalIsOpenImpl: CaseReducer<
DuelState,
PayloadAction<boolean>
> = (state, action) => {
state.modalState.optionModal.isOpen = action.payload;
};
export const resetOptionModalImpl: CaseReducer<DuelState> = (state) => {
state.modalState.optionModal.options = [];
};
// 增加选项
export const fetchOptionMeta = createAsyncThunk(
"duel/fetchOptionMeta",
async (param: { code: number; response: number }) => {
const meta = await fetchCard(param.code >> 4, true);
const msg = getCardStr(meta, param.code & 0xf) || "[?]";
const response = { msg, response: param.response };
return response;
}
);
export const optionModalCase = (
builder: ActionReducerMapBuilder<DuelState>
) => {
builder.addCase(fetchOptionMeta.fulfilled, (state, action) => {
state.modalState.optionModal.options.push(action.payload);
});
};
export const selectOptionModalIsOpen = (state: RootState) =>
state.duel.modalState.optionModal.isOpen;
export const selectOptionModalOptions = (state: RootState) =>
state.duel.modalState.optionModal.options;
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { RootState } from "@/store";
import { DuelState } from "../mod";
export const setPositionModalIsOpenImpl: CaseReducer<
DuelState,
PayloadAction<boolean>
> = (state, action) => {
state.modalState.positionModal.isOpen = action.payload;
};
export const setPositionModalPositionsImpl: CaseReducer<
DuelState,
PayloadAction<ygopro.CardPosition[]>
> = (state, action) => {
state.modalState.positionModal.positions = action.payload;
};
export const resetPositionModalImpl: CaseReducer<DuelState> = (state) => {
state.modalState.positionModal.isOpen = false;
state.modalState.positionModal.positions = [];
};
export const selectPositionModalIsOpen = (state: RootState) =>
state.duel.modalState.positionModal.isOpen;
export const selectPositionModalPositions = (state: RootState) =>
state.duel.modalState.positionModal.positions;
import {
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { fetchCard } from "@/api/cards";
import { RootState } from "@/store";
import { DuelReducer } from "../generic";
import { DuelState } from "../mod";
type SortCard = ReturnType<
typeof ygopro.StocGameMessage.MsgSortCard.Info.prototype.toObject
>;
export const setSortCardModalIsOpenImpl: DuelReducer<boolean> = (
state,
action
) => {
state.modalState.sortCardModal.isOpen = action.payload;
};
export const resetSortCardModalImpl: CaseReducer<DuelState> = (state) => {
state.modalState.sortCardModal.isOpen = false;
state.modalState.sortCardModal.options = [];
};
export const fetchSortCardMeta = createAsyncThunk(
"duel/fetchSortCardMeta",
async (param: SortCard) => {
const meta = await fetchCard(param.code!, true);
return {
meta,
response: param.response!,
};
}
);
export const sortCardModalCase = (
builder: ActionReducerMapBuilder<DuelState>
) => {
// 这里更合理的做法是`pending`的时候先更新`options`,等`meta`数据返回后再异步更新`meta`
builder.addCase(fetchSortCardMeta.fulfilled, (state, action) => {
state.modalState.sortCardModal.options.push(action.payload);
});
};
export const selectSortCardModal = (state: RootState) =>
state.duel.modalState.sortCardModal;
import {
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
PayloadAction,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { CardMeta, fetchCard } from "@/api/cards";
import { fetchStrings, getStrings } from "@/api/strings";
import { RootState } from "@/store";
import { DuelState } from "../mod";
// 更新YesNo弹窗是否打开状态
export const setYesNoModalIsOpenImpl: CaseReducer<
DuelState,
PayloadAction<boolean>
> = (state, action) => {
state.modalState.yesNoModal.isOpen = action.payload;
};
// 设置YesNo弹窗展示内容
export const fetchYesNoMeta = createAsyncThunk(
"duel/fetchYesNoMeta",
async (param: {
code: number;
location: ygopro.CardLocation;
descCode: number;
textGenerator: (
desc: string,
cardMeta: CardMeta,
cardLocation: ygopro.CardLocation
) => string;
}) => {
const desc = fetchStrings("!system", param.descCode);
const meta = await fetchCard(param.code, true);
// TODO: 国际化文案
return param.textGenerator(desc, meta, param.location);
}
);
export const fetchYesNoMetaWithEffecDesc = createAsyncThunk(
"duel/fetchYesNoMetaWithEffecDesc",
async (effectDesc: number) => {
return getStrings(effectDesc);
}
);
export const YesNoModalCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchYesNoMeta.fulfilled, (state, action) => {
state.modalState.yesNoModal.msg = action.payload;
});
builder.addCase(fetchYesNoMetaWithEffecDesc.fulfilled, (state, action) => {
state.modalState.yesNoModal.msg = action.payload;
});
};
export const selectYesNoModalIsOpen = (state: RootState) =>
state.duel.modalState.yesNoModal.isOpen;
export const selectYesNOModalMsg = (state: RootState) =>
state.duel.modalState.yesNoModal.msg;
import {
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
PayloadAction,
} from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { fetchCard } from "@/api/cards";
import { RootState } from "@/store";
import {
clearIdleInteractivities,
clearPlaceInteractivities,
createAsyncMetaThunk,
DuelFieldState,
extendIdleInteractivities,
extendOccupant,
extendPlaceInteractivity,
Interactivity,
removeOccupant,
removeOverlay,
setPosition,
} from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
type MsgUpdateCounter = ReturnType<
typeof ygopro.StocGameMessage.MsgUpdateCounter.prototype.toObject
>;
export interface MonsterState extends DuelFieldState {}
// 初始化怪兽区状态
export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
state,
action
) => {
const player = action.payload;
const monsters = {
inner: [
{
location: {
controler: player,
location: ygopro.CardZone.MZONE,
},
idleInteractivities: [],
counters: {},
},
{
location: {
controler: player,
location: ygopro.CardZone.MZONE,
},
idleInteractivities: [],
counters: {},
},
{
location: {
controler: player,
location: ygopro.CardZone.MZONE,
},
idleInteractivities: [],
counters: {},
},
{
location: {
controler: player,
location: ygopro.CardZone.MZONE,
},
idleInteractivities: [],
counters: {},
},
{
location: {
controler: player,
location: ygopro.CardZone.MZONE,
},
idleInteractivities: [],
counters: {},
},
{
location: {
controler: player,
location: ygopro.CardZone.MZONE,
},
idleInteractivities: [],
counters: {},
},
{
location: {
controler: player,
location: ygopro.CardZone.MZONE,
},
idleInteractivities: [],
counters: {},
},
],
};
if (judgeSelf(player, state)) {
state.meMonsters = monsters;
} else {
state.opMonsters = monsters;
}
};
export const addMonsterPlaceInteractivitiesImpl: CaseReducer<
DuelState,
PayloadAction<[number, number]>
> = (state, action) => {
const controler = action.payload[0];
const sequence = action.payload[1];
const monsters = judgeSelf(controler, state)
? state.meMonsters
: state.opMonsters;
extendPlaceInteractivity(
monsters,
controler,
sequence,
ygopro.CardZone.MZONE
);
};
export const clearMonsterPlaceInteractivitiesImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
const player = action.payload;
const monsters = judgeSelf(player, state)
? state.meMonsters
: state.opMonsters;
clearPlaceInteractivities(monsters);
};
export const addMonsterIdleInteractivitiesImpl: CaseReducer<
DuelState,
PayloadAction<{
player: number;
sequence: number;
interactivity: Interactivity<number>;
}>
> = (state, action) => {
const monsters = judgeSelf(action.payload.player, state)
? state.meMonsters
: state.opMonsters;
extendIdleInteractivities(
monsters,
action.payload.sequence,
action.payload.interactivity
);
};
export const updateMonsterCountersImpl: CaseReducer<
DuelState,
PayloadAction<MsgUpdateCounter>
> = (state, action) => {
const monsters = judgeSelf(action.payload.location?.controler!, state)
? state.meMonsters
: state.opMonsters;
if (monsters) {
const target = monsters.inner.find(
(_, idx) => idx == action.payload.location?.sequence!
);
if (target) {
const count = action.payload.count!;
const counterType = action.payload.action_type!;
switch (action.payload.action_type!) {
case ygopro.StocGameMessage.MsgUpdateCounter.ActionType.ADD: {
if (counterType in target.counters) {
target.counters[counterType] += count;
} else {
target.counters[counterType] = count;
}
break;
}
case ygopro.StocGameMessage.MsgUpdateCounter.ActionType.REMOVE: {
if (counterType in target.counters) {
target.counters[counterType] -= count;
}
break;
}
default: {
break;
}
}
}
}
};
export const clearMonsterIdleInteractivitiesImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
const monsters = judgeSelf(action.payload, state)
? state.meMonsters
: state.opMonsters;
clearIdleInteractivities(monsters);
};
// 增加怪兽
export const fetchMonsterMeta = createAsyncMetaThunk("duel/fetchMonsterMeta");
// 增加怪兽的超量素材
export const fetchOverlayMeta = createAsyncThunk(
"duel/fetchOverlayMeta",
async (param: {
controler: number;
sequence: number;
overlayCodes: number[];
append?: boolean;
}) => {
const controler = param.controler;
const sequence = param.sequence;
const overlayCodes = param.overlayCodes;
const metas = await Promise.all(
overlayCodes.map(async (id) => {
if (id == 0) {
return { id, data: {}, text: {} };
} else {
return await fetchCard(id, true);
}
})
);
const response = { controler, sequence, metas };
return response;
}
);
export const monsterCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchMonsterMeta.pending, (state, action) => {
// Meta结果没返回之前先更新`ID`
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
const position = action.meta.arg.position;
const code = action.meta.arg.code;
const meta = { id: code, data: {}, text: {} };
if (judgeSelf(controler, state)) {
extendOccupant(state.meMonsters, meta, sequence, position);
} else {
extendOccupant(state.opMonsters, meta, sequence, position);
}
});
builder.addCase(fetchMonsterMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const meta = action.payload.meta;
if (judgeSelf(controler, state)) {
extendOccupant(state.meMonsters, meta, sequence);
} else {
extendOccupant(state.opMonsters, meta, sequence);
}
});
builder.addCase(fetchOverlayMeta.pending, (state, action) => {
// Meta结果没返回之前先更新`ID`
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
const overlayCodes = action.meta.arg.overlayCodes;
const append = action.meta.arg.append;
const metas = overlayCodes.map((id) => {
return { id, data: {}, text: {} };
});
const monsters = judgeSelf(controler, state)
? state.meMonsters
: state.opMonsters;
if (monsters) {
const target = monsters.inner.find((_, idx) => idx == sequence);
if (target && target.occupant) {
if (append) {
target.overlay_materials = (target.overlay_materials || []).concat(
metas
);
} else {
target.overlay_materials = metas;
}
}
}
});
builder.addCase(fetchOverlayMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const overlayMetas = action.payload.metas;
const monsters = judgeSelf(controler, state)
? state.meMonsters
: state.opMonsters;
if (monsters) {
const target = monsters.inner.find((_, idx) => idx == sequence);
for (const meta of overlayMetas) {
for (const overlay of target?.overlay_materials || []) {
if (meta.id == overlay.id) {
overlay.data = meta.data;
overlay.text = meta.text;
}
}
}
}
});
};
// 删除怪兽
export const removeMonsterImpl: CaseReducer<
DuelState,
PayloadAction<{ controler: number; sequence: number }>
> = (state, action) => {
const controler = action.payload.controler;
const monsters = judgeSelf(controler, state)
? state.meMonsters
: state.opMonsters;
removeOccupant(monsters, action.payload.sequence);
removeOverlay(monsters, action.payload.sequence);
};
// 删除超量素材
export const removeOverlayImpl: CaseReducer<
DuelState,
PayloadAction<{
controler: number;
sequence: number;
overlaySequence: number;
}>
> = (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const overlaySequence = action.payload.overlaySequence;
const monsters = judgeSelf(controler, state)
? state.meMonsters
: state.opMonsters;
if (monsters) {
const target = monsters.inner.find((_, idx) => idx == sequence);
if (target && target.overlay_materials) {
target.overlay_materials = target.overlay_materials.filter(
(_, idx) => idx != overlaySequence
);
}
}
};
// 改变怪兽表示形式
export const setMonsterPositionImpl: CaseReducer<
DuelState,
PayloadAction<{
controler: number;
sequence: number;
position: ygopro.CardPosition;
}>
> = (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const position = action.payload.position;
const monsters = judgeSelf(controler, state)
? state.meMonsters
: state.opMonsters;
setPosition(monsters, sequence, position);
};
export const selectMeMonsters = (state: RootState) =>
state.duel.meMonsters || { inner: [] };
export const selectOpMonsters = (state: RootState) =>
state.duel.opMonsters || { inner: [] };
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "@/store";
import { DuelState } from "./mod";
export interface PhaseState {
currentPhase: string; // 当前的阶段
enableBp: boolean; // 允许进入战斗阶段
enableM2: boolean; // 允许进入M2阶段
enableEp: boolean; // 允许回合结束
}
export const newPhaseImpl: CaseReducer<DuelState, PayloadAction<string>> = (
state,
action
) => {
if (state.phase) {
state.phase.currentPhase = action.payload;
} else {
state.phase = {
currentPhase: action.payload,
enableBp: false,
enableM2: false,
enableEp: false,
};
}
};
export const setEnableBpImpl: CaseReducer<DuelState, PayloadAction<boolean>> = (
state,
action
) => {
if (state.phase) {
state.phase.enableBp = action.payload;
}
};
export const setEnableM2Impl: CaseReducer<DuelState, PayloadAction<boolean>> = (
state,
action
) => {
if (state.phase) {
state.phase.enableM2 = action.payload;
}
};
export const setEnableEpImpl: CaseReducer<DuelState, PayloadAction<boolean>> = (
state,
action
) => {
if (state.phase) {
state.phase.enableEp = action.payload;
}
};
export const selectCurrentPhase = (state: RootState) =>
state.duel.phase?.currentPhase;
export const selectEnableBp = (state: RootState) =>
state.duel.phase?.enableBp || false;
export const selectEnableM2 = (state: RootState) =>
state.duel.phase?.enableBp || false;
export const selectEnableEp = (state: RootState) =>
state.duel.phase?.enableEp || false;
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface TimeLimit {
leftTime: number;
}
// 更新计时
export const updateTimeLimitImpl: CaseReducer<
DuelState,
PayloadAction<[number, number]>
> = (state, action) => {
const player = action.payload[0];
const leftTime = action.payload[1];
if (judgeSelf(player, state)) {
state.meTimeLimit = { leftTime };
} else {
state.opTimeLimit = { leftTime };
}
};
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "@/store";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export const newTurnImpl: CaseReducer<DuelState, PayloadAction<number>> = (
state,
action
) => {
state.currentPlayer = action.payload;
};
export const selectCurrentPlayerIsMe = (state: RootState) =>
judgeSelf(state.duel.currentPlayer!, state.duel);
/*
* 对局内状态更新逻辑的一些共用函数和数据结构
*
* */
import { Draft } from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { CardState } from "./generic";
import { DuelState } from "./mod";
type Location =
| ygopro.CardLocation
| ReturnType<typeof ygopro.CardLocation.prototype.toObject>;
/*
* 通过`player`和`selfType`判断是应该处理自己还是对手
* */
export function judgeSelf(player: number, state: Draft<DuelState>): boolean {
const selfType = state.selfType;
if (selfType === 1) {
// 自己是先攻
return player == 0;
} else if (selfType === 2) {
// 自己是后攻
return player == 1;
} else {
// currently never reach
return false;
}
}
/*
* 通过`controler`,`zone`和`sequence`获取卡牌状态*/
export function findCardByLocation(
state: Draft<DuelState>,
location: Location
): CardState | undefined {
const controler = location.controler!;
const zone = location.location;
const sequence = location.sequence;
const finder = (_: any, idx: number) => idx == sequence;
switch (zone) {
case ygopro.CardZone.HAND: {
const hands = judgeSelf(controler, state) ? state.meHands : state.opHands;
return hands?.inner.find(finder);
}
case ygopro.CardZone.MZONE: {
const monsters = judgeSelf(controler, state)
? state.meMonsters
: state.opMonsters;
return monsters?.inner.find(finder);
}
case ygopro.CardZone.SZONE: {
const magics = judgeSelf(controler, state)
? state.meMagics
: state.opMagics;
return magics?.inner.find(finder);
}
case ygopro.CardZone.REMOVED: {
const banishedZones = judgeSelf(controler, state)
? state.meBanishedZone
: state.opBanishedZone;
return banishedZones?.inner.find(finder);
}
case ygopro.CardZone.GRAVE: {
const cemerety = judgeSelf(controler, state)
? state.meGraveyard
: state.opGraveyard;
return cemerety?.inner.find(finder);
}
default: {
return undefined;
}
}
}
export function cmpCardLocation(
left: Location,
right?: Location,
strict?: boolean
): boolean {
if (strict) {
return JSON.stringify(left) === JSON.stringify(right);
} else {
return (
left.controler === right?.controler &&
left.location === right?.location &&
left.sequence === right?.sequence
);
}
}
/*
* 加入房间状态更新逻辑
*
* */
import { createSlice } from "@reduxjs/toolkit";
import { RootState } from "@/store";
export interface JoinState {
value: boolean;
}
const initialState: JoinState = {
value: false,
};
const joinedSlice = createSlice({
name: "join",
initialState,
reducers: {
setJoined: (state) => {
state.value = true;
},
setUnJoined: (state) => {
state.value = false;
},
},
});
export const { setJoined, setUnJoined } = joinedSlice.actions;
export const selectJoined = (state: RootState) => state.join.value;
export default joinedSlice.reducer;
/*
* 猜拳页面的状态更新逻辑
*
* */
import { createSlice } from "@reduxjs/toolkit";
import { RootState } from "@/store";
export interface moraState {
duelStart: boolean;
selectHandAble: boolean;
selectTpAble: boolean;
}
const initialState: moraState = {
duelStart: false,
selectHandAble: false,
selectTpAble: false,
};
const moraSlice = createSlice({
name: "mora",
initialState,
reducers: {
duelStart: (state) => {
state.duelStart = true;
},
selectHandAble: (state) => {
state.selectHandAble = true;
},
unSelectHandAble: (state) => {
state.selectHandAble = false;
},
selectTpAble: (state) => {
state.selectTpAble = true;
},
unSelectTpAble: (state) => {
state.selectTpAble = false;
},
},
});
export const {
duelStart,
selectHandAble,
unSelectHandAble,
selectTpAble,
unSelectTpAble,
} = moraSlice.actions;
export const selectDuelStart = (state: RootState) => state.mora.duelStart;
export const selectHandSelectAble = (state: RootState) =>
state.mora.selectHandAble;
export const selectTpSelectAble = (state: RootState) => state.mora.selectTpAble;
export default moraSlice.reducer;
/*
* 进入房间的玩家状态更新逻辑
*
* */
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "@/store";
export interface Player {
name?: string;
state?: string;
isHost?: boolean;
deckInfo?: deckInfo;
}
export interface deckInfo {
mainCnt: number;
extraCnt: number;
sideCnt: number;
}
export interface playerState {
player0: Player;
player1: Player;
observerCount: number;
isHost: boolean;
}
const initialState: playerState = {
player0: {},
player1: {},
observerCount: 0,
isHost: false,
};
const playerSlice = createSlice({
name: "player",
initialState,
reducers: {
player0Enter: (state, action: PayloadAction<string>) => {
state.player0.name = action.payload;
},
player1Enter: (state, action: PayloadAction<string>) => {
state.player1.name = action.payload;
},
player0Update: (state, action: PayloadAction<string>) => {
state.player0.state = action.payload;
},
player1Update: (state, action: PayloadAction<string>) => {
state.player1.state = action.payload;
},
player0Leave: (state) => {
state.player0 = {};
},
player1Leave: (state) => {
state.player1 = {};
},
player0DeckInfo: (state, action: PayloadAction<deckInfo>) => {
state.player0.deckInfo = action.payload;
},
player1DeckInfo: (state, action: PayloadAction<deckInfo>) => {
state.player1.deckInfo = action.payload;
},
hostChange: (state, action: PayloadAction<number>) => {
const i = action.payload;
if (i === 0) {
state.player0.isHost = true;
state.player1.isHost = false;
} else {
state.player1.isHost = true;
state.player0.isHost = false;
}
},
observerIncrement: (state) => {
state.observerCount += 1;
},
observerChange: (state, action: PayloadAction<number>) => {
state.observerCount = action.payload;
},
updateIsHost: (state, action: PayloadAction<boolean>) => {
state.isHost = action.payload;
},
},
});
export const {
player0Enter,
player1Enter,
player0Update,
player1Update,
player0Leave,
player1Leave,
player0DeckInfo,
player1DeckInfo,
hostChange,
observerIncrement,
observerChange,
updateIsHost,
} = playerSlice.actions;
export const selectPlayer0 = (state: RootState) => state.player.player0;
export const selectPlayer1 = (state: RootState) => state.player.player1;
export const selectIsHost = (state: RootState) => state.player.isHost;
export const selectObserverCount = (state: RootState) =>
state.player.observerCount;
export default playerSlice.reducer;
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
import { fetchEsHintMeta } from "@/stores";
export default (
attack: ygopro.StocGameMessage.MsgAttack,
dispatch: AppDispatch
) => {
// dispatch(
// fetchEsHintMeta({ originMsg: "「[?]」攻击时", location: attack.location })
// );
FIXME_fetchEsHintMeta({
export default (attack: ygopro.StocGameMessage.MsgAttack) => {
fetchEsHintMeta({
originMsg: "「[?]」攻击时",
location: attack.location,
});
......
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import {
fetchEsHintMeta as FIXME_fetchEsHintMeta,
matStore,
} from "@/valtioStores";
import { fetchEsHintMeta } from "@/stores";
export default (
_: ygopro.StocGameMessage.MsgAttackDisabled,
dispatch: AppDispatch
) => {
// dispatch(fetchEsHintMeta({ originMsg: "攻击被无效时" }));
FIXME_fetchEsHintMeta({ originMsg: "攻击被无效时" });
export default (_: ygopro.StocGameMessage.MsgAttackDisabled) => {
fetchEsHintMeta({ originMsg: "攻击被无效时" });
};
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import {
fetchEsHintMeta as FIXME_fetchEsHintMeta,
matStore,
} from "@/valtioStores";
export default (
chaining: ygopro.StocGameMessage.MsgChaining,
dispatch: AppDispatch
) => {
// dispatch(
// fetchEsHintMeta({ originMsg: "「[?]」被发动时", cardID: chaining.code })
// );
FIXME_fetchEsHintMeta({
import { fetchEsHintMeta } from "@/stores";
export default (chaining: ygopro.StocGameMessage.MsgChaining) => {
fetchEsHintMeta({
originMsg: "「[?]」被发动时",
cardID: chaining.code,
});
......
import { ygopro } from "@/api";
import { fetchHandsMeta } from "@/reducers/duel/handsSlice";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import {
fetchEsHintMeta as FIXME_fetchEsHintMeta,
matStore,
} from "@/valtioStores";
import { fetchEsHintMeta, matStore } from "@/stores";
export default (
draw: ygopro.StocGameMessage.MsgDraw,
dispatch: AppDispatch
) => {
// dispatch(fetchEsHintMeta({ originMsg: "玩家抽卡时" }));
// dispatch(fetchHandsMeta({ controler: draw.player, codes: draw.cards }));
FIXME_fetchEsHintMeta({ originMsg: "玩家抽卡时" });
export default (draw: ygopro.StocGameMessage.MsgDraw) => {
fetchEsHintMeta({ originMsg: "玩家抽卡时" });
matStore.hands.of(draw.player).add(draw.cards);
};
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
import { fetchEsHintMeta } from "@/stores";
export default (
_: ygopro.StocGameMessage.MsgFlipSummoned,
dispatch: AppDispatch
) => {
// dispatch(fetchEsHintMeta({ originMsg: 1608 }));
FIXME_fetchEsHintMeta({ originMsg: 1608 });
export default (_: ygopro.StocGameMessage.MsgFlipSummoned) => {
fetchEsHintMeta({ originMsg: 1608 });
};
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
import { fetchEsHintMeta } from "@/stores";
export default (
flipSummoning: ygopro.StocGameMessage.MsgFlipSummoning,
dispatch: AppDispatch
) => {
// dispatch(
// fetchEsHintMeta({
// originMsg: "「[?]」反转召唤宣言时",
// cardID: flipSummoning.code,
// })
// );
FIXME_fetchEsHintMeta({
export default (flipSummoning: ygopro.StocGameMessage.MsgFlipSummoning) => {
fetchEsHintMeta({
originMsg: "「[?]」反转召唤宣言时",
cardID: flipSummoning.code,
});
......
import { ygopro } from "@/api";
import { setWaiting } from "@/reducers/duel/mod";
import { store } from "@/store";
import { matStore } from "@/valtioStores";
import { matStore } from "@/stores";
import onMsgAttack from "./attack";
import onMsgAttackDisable from "./attackDisable";
......@@ -58,212 +56,210 @@ const ActiveList = [
];
export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
const msg = pb.stoc_game_msg;
if (ActiveList.includes(msg.gameMsg)) {
dispatch(setWaiting(false));
matStore.waiting = false;
}
switch (msg.gameMsg) {
case "start": {
onMsgStart(msg.start, dispatch);
onMsgStart(msg.start);
break;
}
case "draw": {
onMsgDraw(msg.draw, dispatch);
onMsgDraw(msg.draw);
break;
}
case "new_turn": {
onMsgNewTurn(msg.new_turn, dispatch);
onMsgNewTurn(msg.new_turn);
break;
}
case "new_phase": {
onMsgNewPhase(msg.new_phase, dispatch);
onMsgNewPhase(msg.new_phase);
break;
}
case "hint": {
onMsgHint(msg.hint, dispatch);
onMsgHint(msg.hint);
break;
}
case "select_idle_cmd": {
onMsgSelectIdleCmd(msg.select_idle_cmd, dispatch);
onMsgSelectIdleCmd(msg.select_idle_cmd);
break;
}
case "select_place": {
onMsgSelectPlace(msg.select_place, dispatch);
onMsgSelectPlace(msg.select_place);
break;
}
case "move": {
onMsgMove(msg.move, dispatch);
onMsgMove(msg.move);
break;
}
case "select_card": {
onMsgSelectCard(msg.select_card, dispatch);
onMsgSelectCard(msg.select_card);
break;
}
case "select_chain": {
onMsgSelectChain(msg.select_chain, dispatch);
onMsgSelectChain(msg.select_chain);
break;
}
case "select_effect_yn": {
onMsgSelectEffectYn(msg.select_effect_yn, dispatch);
onMsgSelectEffectYn(msg.select_effect_yn);
break;
}
case "select_position": {
onMsgSelectPosition(msg.select_position, dispatch);
onMsgSelectPosition(msg.select_position);
break;
}
case "select_option": {
onMsgSelectOption(msg.select_option, dispatch);
onMsgSelectOption(msg.select_option);
break;
}
case "shuffle_hand": {
onMsgShuffleHand(msg.shuffle_hand, dispatch);
onMsgShuffleHand(msg.shuffle_hand);
break;
}
case "select_battle_cmd": {
onMsgSelectBattleCmd(msg.select_battle_cmd, dispatch);
onMsgSelectBattleCmd(msg.select_battle_cmd);
break;
}
case "pos_change": {
onMsgPosChange(msg.pos_change, dispatch);
onMsgPosChange(msg.pos_change);
break;
}
case "select_unselect_card": {
onMsgSelectUnselectCard(msg.select_unselect_card, dispatch);
onMsgSelectUnselectCard(msg.select_unselect_card);
break;
}
case "select_yes_no": {
onMsgSelectYesNo(msg.select_yes_no, dispatch);
onMsgSelectYesNo(msg.select_yes_no);
break;
}
case "update_hp": {
onMsgUpdateHp(msg.update_hp, dispatch);
onMsgUpdateHp(msg.update_hp);
break;
}
case "win": {
onMsgWin(msg.win, dispatch);
onMsgWin(msg.win);
break;
}
case "wait": {
onMsgWait(msg.wait, dispatch);
onMsgWait(msg.wait);
break;
}
case "update_data": {
onMsgUpdateData(msg.update_data, dispatch);
onMsgUpdateData(msg.update_data);
break;
}
case "reload_field": {
onMsgReloadField(msg.reload_field, dispatch);
onMsgReloadField(msg.reload_field);
break;
}
case "select_sum": {
onMsgSelectSum(msg.select_sum, dispatch);
onMsgSelectSum(msg.select_sum);
break;
}
case "select_tribute": {
onMsgSelectTribute(msg.select_tribute, dispatch);
onMsgSelectTribute(msg.select_tribute);
break;
}
case "update_counter": {
onMsgUpdateCounter(msg.update_counter, dispatch);
onMsgUpdateCounter(msg.update_counter);
break;
}
case "select_counter": {
onMsgSelectCounter(msg.select_counter, dispatch);
onMsgSelectCounter(msg.select_counter);
break;
}
case "sort_card": {
onMsgSortCard(msg.sort_card, dispatch);
onMsgSortCard(msg.sort_card);
break;
}
case "set": {
onMsgSet(msg.set, dispatch);
onMsgSet(msg.set);
break;
}
case "swap": {
onMsgSwap(msg.swap, dispatch);
onMsgSwap(msg.swap);
break;
}
case "attack": {
onMsgAttack(msg.attack, dispatch);
onMsgAttack(msg.attack);
break;
}
case "attack_disable": {
onMsgAttackDisable(msg.attack_disable, dispatch);
onMsgAttackDisable(msg.attack_disable);
break;
}
case "chaining": {
onMsgChaining(msg.chaining, dispatch);
onMsgChaining(msg.chaining);
break;
}
case "summoning": {
onMsgSummoning(msg.summoning, dispatch);
onMsgSummoning(msg.summoning);
break;
}
case "summoned": {
onMsgSummoned(msg.summoned, dispatch);
onMsgSummoned(msg.summoned);
break;
}
case "flip_summoning": {
onMsgFlipSummoning(msg.flip_summoning, dispatch);
onMsgFlipSummoning(msg.flip_summoning);
break;
}
case "flip_summoned": {
onMsgFilpSummoned(msg.flip_summoned, dispatch);
onMsgFilpSummoned(msg.flip_summoned);
break;
}
case "sp_summoning": {
onMsgSpSummoning(msg.sp_summoning, dispatch);
onMsgSpSummoning(msg.sp_summoning);
break;
}
case "sp_summoned": {
onMsgSpSummoned(msg.sp_summoned, dispatch);
onMsgSpSummoned(msg.sp_summoned);
break;
}
case "unimplemented": {
onUnimplemented(msg.unimplemented, dispatch);
onUnimplemented(msg.unimplemented);
break;
}
......
......@@ -3,33 +3,22 @@ import {
fetchCommonHintMeta,
fetchEsHintMeta,
fetchSelectHintMeta,
} from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import {
fetchCommonHintMeta as FIXME_fetchCommonHintMeta,
fetchEsHintMeta as FIXME_fetchEsHintMeta,
fetchSelectHintMeta as FIXME_fetchSelectHintMeta,
} from "@/valtioStores";
} from "@/stores";
import MsgHint = ygopro.StocGameMessage.MsgHint;
export default (hint: MsgHint, dispatch: AppDispatch) => {
export default (hint: MsgHint) => {
switch (hint.hint_type) {
case MsgHint.HintType.HINT_EVENT: {
// dispatch(fetchEsHintMeta({ originMsg: hint.hint_data }));
FIXME_fetchEsHintMeta({ originMsg: hint.hint_data });
fetchEsHintMeta({ originMsg: hint.hint_data });
break;
}
case MsgHint.HintType.HINT_MESSAGE: {
// dispatch(fetchCommonHintMeta(hint.hint_data));
FIXME_fetchCommonHintMeta(hint.hint_data);
fetchCommonHintMeta(hint.hint_data);
break;
}
case MsgHint.HintType.HINT_SELECTMSG: {
// dispatch(
// fetchSelectHintMeta({ selectHintData: hint.hint_data, esHint: "" })
// );
FIXME_fetchSelectHintMeta({
fetchSelectHintMeta({
selectHintData: hint.hint_data,
esHint: "",
});
......
import { ygopro } from "@/api";
import MsgMove = ygopro.StocGameMessage.MsgMove;
import { fetchBanishedZoneMeta } from "@/reducers/duel/banishedZoneSlice";
import { fetchExtraDeckMeta } from "@/reducers/duel/extraDeckSlice";
import { fetchGraveyardMeta } from "@/reducers/duel/graveyardSlice";
import { insertHandMeta } from "@/reducers/duel/handsSlice";
import { fetchMagicMeta } from "@/reducers/duel/magicSlice";
import {
removeBanishedZone,
removeExtraDeck,
removeGraveyard,
removeHand,
removeMagic,
removeMonster,
removeOverlay,
} from "@/reducers/duel/mod";
import {
fetchMonsterMeta,
fetchOverlayMeta,
} from "@/reducers/duel/monstersSlice";
import { AppDispatch } from "@/store";
import {
fetchOverlayMeta as FIXME_fetchOverlayMeta,
valtioStore,
} from "@/valtioStores";
import { fetchOverlayMeta, store } from "@/stores";
type MsgMove = ygopro.StocGameMessage.MsgMove;
import { REASON_MATERIAL } from "../../common";
const { matStore } = valtioStore;
const { matStore } = store;
const OVERLAY_STACK: { code: number; sequence: number }[] = [];
export default (move: MsgMove, dispatch: AppDispatch) => {
export default (move: MsgMove) => {
const code = move.code;
const from = move.from;
const to = move.to;
......@@ -80,7 +58,7 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
const sorted = overlayMetarials
.sort((a, b) => a.sequence - b.sequence)
.map((overlay) => overlay.code);
FIXME_fetchOverlayMeta(to.controler, to.sequence, sorted);
fetchOverlayMeta(to.controler, to.sequence, sorted);
// 设置Occupant,和魔陷区/其他区共用一个逻辑,特地不写break
}
case ygopro.CardZone.SZONE: {
......@@ -104,7 +82,7 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
OVERLAY_STACK.push({ code, sequence: to.overlay_sequence });
} else {
// 其他情况下,比如“宵星的机神 丁吉尔苏”的“补充超量素材”效果,直接更新状态中
FIXME_fetchOverlayMeta(to.controler, to.sequence, [code], true);
fetchOverlayMeta(to.controler, to.sequence, [code], true);
}
break;
}
......@@ -113,163 +91,4 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
break;
}
}
// switch (from.location) {
// case ygopro.CardZone.HAND: {
// dispatch(removeHand([from.controler, from.sequence]));
// break;
// }
// case ygopro.CardZone.MZONE: {
// dispatch(
// removeMonster({ controler: from.controler, sequence: from.sequence })
// );
// break;
// }
// case ygopro.CardZone.SZONE: {
// dispatch(
// removeMagic({ controler: from.controler, sequence: from.sequence })
// );
// break;
// }
// case ygopro.CardZone.GRAVE: {
// dispatch(
// removeGraveyard({ controler: from.controler, sequence: from.sequence })
// );
// break;
// }
// case ygopro.CardZone.REMOVED: {
// dispatch(
// removeBanishedZone({
// controler: from.controler,
// sequence: from.sequence,
// })
// );
// break;
// }
// case ygopro.CardZone.EXTRA: {
// dispatch(
// removeExtraDeck({ controler: from.controler, sequence: from.sequence })
// );
// break;
// }
// case ygopro.CardZone.OVERLAY: {
// dispatch(
// removeOverlay({
// controler: from.controler,
// sequence: from.sequence,
// overlaySequence: from.overlay_sequence,
// })
// );
// break;
// }
// default: {
// console.log(`Unhandled zone type ${from.location}`);
// break;
// }
// }
// switch (to.location) {
// case ygopro.CardZone.MZONE: {
// dispatch(
// fetchMonsterMeta({
// controler: to.controler,
// sequence: to.sequence,
// position: to.position,
// code,
// })
// );
// // 处理超量素材
// const overlayMetarials = OVERLAY_STACK.splice(0, OVERLAY_STACK.length);
// let sorted = overlayMetarials
// .sort((a, b) => a.sequence - b.sequence)
// .map((overlay) => overlay.code);
// dispatch(
// fetchOverlayMeta({
// controler: to.controler,
// sequence: to.sequence,
// overlayCodes: sorted,
// })
// );
// break;
// }
// case ygopro.CardZone.SZONE: {
// dispatch(
// fetchMagicMeta({
// controler: to.controler,
// sequence: to.sequence,
// position: to.position,
// code,
// })
// );
// break;
// }
// case ygopro.CardZone.GRAVE: {
// dispatch(
// fetchGraveyardMeta({
// controler: to.controler,
// sequence: to.sequence,
// code,
// })
// );
// break;
// }
// case ygopro.CardZone.HAND: {
// dispatch(
// insertHandMeta({ controler: to.controler, sequence: to.sequence, code })
// );
// break;
// }
// case ygopro.CardZone.REMOVED: {
// dispatch(
// fetchBanishedZoneMeta({
// controler: to.controler,
// sequence: to.sequence,
// code,
// })
// );
// break;
// }
// case ygopro.CardZone.EXTRA: {
// dispatch(
// fetchExtraDeckMeta({
// controler: to.controler,
// sequence: to.sequence,
// code,
// })
// );
// break;
// }
// case ygopro.CardZone.OVERLAY: {
// if (reason == REASON_MATERIAL) {
// // 超量素材在进行超量召唤时,若玩家未选择超量怪兽的位置,会“沉到决斗盘下面”,`reason`字段值是`REASON_MATERIAL`
// // 这时候将它们放到一个栈中,待超量怪兽的Move消息到来时从栈中获取超量素材补充到状态中
// OVERLAY_STACK.push({ code, sequence: to.overlay_sequence });
// } else {
// // 其他情况下,比如“宵星的机神 丁吉尔苏”的“补充超量素材”效果,直接更新状态中
// dispatch(
// fetchOverlayMeta({
// controler: to.controler,
// sequence: to.sequence,
// overlayCodes: [code],
// append: true,
// })
// );
// }
// break;
// }
// default: {
// console.log(`Unhandled zone type ${to.location}`);
// break;
// }
// }
};
import { ygopro } from "@/api";
import { updatePhase } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { matStore, type PhaseName } from "@/valtioStores";
export default (
newPhase: ygopro.StocGameMessage.MsgNewPhase,
dispatch: AppDispatch
) => {
// dispatch(
// updatePhase(
// ygopro.StocGameMessage.MsgNewPhase.PhaseType[newPhase.phase_type]
// )
// );
import { matStore, type PhaseName } from "@/stores";
export default (newPhase: ygopro.StocGameMessage.MsgNewPhase) => {
// ts本身还没有这么智能,所以需要手动指定类型
const currentPhase = ygopro.StocGameMessage.MsgNewPhase.PhaseType[
newPhase.phase_type
] as PhaseName;
matStore.phase.currentPhase = currentPhase;
};
import { ygopro } from "@/api";
import { updateTurn } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { matStore } from "@/valtioStores";
import { matStore } from "@/stores";
export default (
newTurn: ygopro.StocGameMessage.MsgNewTurn,
dispatch: AppDispatch
) => {
export default (newTurn: ygopro.StocGameMessage.MsgNewTurn) => {
const player = newTurn.player;
// dispatch(updateTurn(player));
matStore.currentPlayer = player;
};
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { setMagicPosition, setMonsterPosition } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import MsgPosChange = ygopro.StocGameMessage.MsgPosChange;
import {
fetchEsHintMeta as FIXME_fetchEsHintMeta,
matStore,
} from "@/valtioStores";
export default (posChange: MsgPosChange, dispatch: AppDispatch) => {
import { fetchEsHintMeta, matStore } from "@/stores";
export default (posChange: MsgPosChange) => {
const { location, controler, sequence } = posChange.card_info;
switch (location) {
case ygopro.CardZone.MZONE: {
// dispatch(
// setMonsterPosition({
// controler: controler,
// sequence,
// position: posChange.cur_position,
// })
// );
matStore.monsters.of(controler)[sequence].location.position =
posChange.cur_position;
break;
}
case ygopro.CardZone.SZONE: {
// dispatch(
// setMagicPosition({
// controler: controler,
// sequence,
// position: posChange.cur_position,
// })
// );
matStore.magics.of(controler)[sequence].location.position =
posChange.cur_position;
......@@ -41,9 +21,7 @@ export default (posChange: MsgPosChange, dispatch: AppDispatch) => {
console.log(`Unhandled zone ${location}`);
}
}
// dispatch(fetchEsHintMeta({ originMsg: 1600 }));
FIXME_fetchEsHintMeta({
fetchEsHintMeta({
originMsg: 1600,
});
};
import { ygopro } from "@/api";
import { reloadField } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { matStore } from "@/valtioStores";
import { matStore } from "@/stores";
type MsgReloadField = ygopro.StocGameMessage.MsgReloadField;
type ZoneActions = ygopro.StocGameMessage.MsgReloadField.ZoneAction[];
export default (field: MsgReloadField, dispatch: AppDispatch) => {
// dispatch(reloadField(field));
const _duel_rule = field.duel_rule;
export default (field: MsgReloadField) => {
const _duel_rule = field.duel_rule; // TODO: duel_rule
const gamers = ["me", "op"] as const;
gamers.forEach((gamer) => {
......
import { ActionCreatorWithPayload } from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { Interactivity, InteractType } from "@/reducers/duel/generic";
import {
addHandsIdleInteractivity,
addMagicIdleInteractivities,
addMonsterIdleInteractivities,
clearAllIdleInteractivities,
setEnableEp,
setEnableM2,
} from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import {
clearAllIdleInteractivities as FIXME_clearAllIdleInteractivities,
clearAllIdleInteractivities as clearAllIdleInteractivities,
type Interactivity,
InteractType,
matStore,
} from "@/valtioStores";
} from "@/stores";
import MsgSelectBattleCmd = ygopro.StocGameMessage.MsgSelectBattleCmd;
export default (selectBattleCmd: MsgSelectBattleCmd, dispatch: AppDispatch) => {
export default (selectBattleCmd: MsgSelectBattleCmd) => {
const player = selectBattleCmd.player;
const cmds = selectBattleCmd.battle_cmds;
// 先清掉之前的互动性
// dispatch(clearAllIdleInteractivities(player));
FIXME_clearAllIdleInteractivities(player);
// const dispatcher = (
// battleData: MsgSelectBattleCmd.BattleCmd.BattleData,
// interactType: InteractType | undefined,
// actionCreator: ActionCreatorWithPayload<
// {
// player: number;
// sequence: number;
// interactivity: Interactivity<number>;
// },
// string
// >
// ) => {
// const cardInfo = battleData.card_info;
// if (interactType === InteractType.ACTIVATE) {
// dispatch(
// actionCreator({
// player,
// sequence: cardInfo.sequence,
// interactivity: {
// interactType,
// activateIndex: battleData.effect_description,
// response: battleData.response,
// },
// })
// );
// } else if (interactType === InteractType.ATTACK) {
// dispatch(
// actionCreator({
// player,
// sequence: cardInfo.sequence,
// interactivity: {
// interactType,
// directAttackAble: battleData.direct_attackable,
// response: battleData.response,
// },
// })
// );
// } else {
// console.log(`Unhandled InteractType`);
// }
// };
clearAllIdleInteractivities(player);
cmds.forEach((cmd) => {
const interactType = battleTypeToInteracType(cmd.battle_type);
......@@ -83,7 +29,7 @@ export default (selectBattleCmd: MsgSelectBattleCmd, dispatch: AppDispatch) => {
[InteractType.ACTIVATE]: { activateIndex: data.effect_description },
[InteractType.ATTACK]: { directAttackAble: data.direct_attackable },
};
const tmp = map[interactType];
const tmp = map[interactType]; // 添加额外信息
matStore
.in(location)
.of(player)
......@@ -95,34 +41,8 @@ export default (selectBattleCmd: MsgSelectBattleCmd, dispatch: AppDispatch) => {
} else {
console.warn(`Undefined InteractType`);
}
// >>> 从这开始删除 >>>
// switch (location) {
// case ygopro.CardZone.HAND: {
// dispatcher(data, interactType, addHandsIdleInteractivity);
// break;
// }
// case ygopro.CardZone.MZONE: {
// dispatcher(data, interactType, addMonsterIdleInteractivities);
// break;
// }
// case ygopro.CardZone.SZONE: {
// dispatcher(data, interactType, addMagicIdleInteractivities);
// break;
// }
// default: {
// }
// }
// <<< 到这结束删除,上面的const dispatcher也删掉 <<<
});
});
// dispatch(setEnableM2(selectBattleCmd.enable_m2));
// dispatch(setEnableEp(selectBattleCmd.enable_ep));
matStore.phase.enableM2 = selectBattleCmd.enable_m2;
matStore.phase.enableEp = selectBattleCmd.enable_ep;
};
......
import { ygopro } from "@/api";
import {
setCheckCardModalIsOpen,
setCheckCardModalMinMax,
setCheckCardModalOnSubmit,
} from "@/reducers/duel/mod";
import { fetchCheckCardMeta } from "@/reducers/duel/modal/mod";
import { AppDispatch } from "@/store";
import MsgSelectCard = ygopro.StocGameMessage.MsgSelectCard;
import {
fetchCheckCardMeta as FIXME_fetchCheckCardMeta,
messageStore,
} from "@/valtioStores";
import { fetchCheckCardMeta, messageStore } from "@/stores";
import { CardZoneToChinese } from "./util";
export default (selectCard: MsgSelectCard, dispatch: AppDispatch) => {
export default (selectCard: MsgSelectCard) => {
const _player = selectCard.player;
const _cancelable = selectCard.cancelable; // TODO: 处理可取消逻辑
const min = selectCard.min;
......@@ -22,35 +12,17 @@ export default (selectCard: MsgSelectCard, dispatch: AppDispatch) => {
const cards = selectCard.cards;
// TODO: handle release_param
// dispatch(setCheckCardModalMinMax({ min, max }));
// dispatch(setCheckCardModalOnSubmit("sendSelectCardResponse"));
messageStore.checkCardModal.selectMin = min;
messageStore.checkCardModal.selectMax = max;
messageStore.checkCardModal.onSubmit = "sendSelectCardResponse";
for (const card of cards) {
const tagName = CardZoneToChinese(card.location.location);
// dispatch(
// fetchCheckCardMeta({
// tagName,
// option: {
// code: card.code,
// location: card.location,
// response: card.response,
// },
// })
// );
// FIXME: rename
FIXME_fetchCheckCardMeta(card.location.location, {
fetchCheckCardMeta(card.location.location, {
code: card.code,
location: card.location,
response: card.response,
});
}
// dispatch(setCheckCardModalIsOpen(true));
messageStore.checkCardModal.isOpen = true;
};
import { ygopro } from "@/api";
import { sendSelectChainResponse } from "@/api/ocgcore/ocgHelper";
import { fetchSelectHintMeta } from "@/reducers/duel/hintSlice";
import { sendSelectChainResponse, ygopro } from "@/api";
import {
setCheckCardMOdalCancelAble,
setCheckCardModalCancelResponse,
setCheckCardModalIsOpen,
setCheckCardModalMinMax,
setCheckCardModalOnSubmit,
} from "@/reducers/duel/mod";
import { fetchCheckCardMeta } from "@/reducers/duel/modal/mod";
import { AppDispatch } from "@/store";
import {
fetchCheckCardMeta as FIXME_fetchCheckCardMeta,
fetchSelectHintMeta as FIXME_fetchSelectHintMeta,
fetchCheckCardMeta,
fetchSelectHintMeta,
messageStore,
} from "@/valtioStores";
import { CardZoneToChinese } from "./util";
} from "@/stores";
type MsgSelectChain = ygopro.StocGameMessage.MsgSelectChain;
export default (selectChain: MsgSelectChain, dispatch: AppDispatch) => {
export default (selectChain: MsgSelectChain) => {
const player = selectChain.player;
const spCount = selectChain.special_count;
const forced = selectChain.forced;
......@@ -71,11 +58,6 @@ export default (selectChain: MsgSelectChain, dispatch: AppDispatch) => {
case 3: {
// 处理强制发动的卡
// dispatch(setCheckCardModalMinMax({ min: 1, max: 1 }));
// dispatch(setCheckCardModalOnSubmit("sendSelectChainResponse"));
// dispatch(setCheckCardMOdalCancelAble(!forced));
// dispatch(setCheckCardModalCancelResponse(-1));
messageStore.checkCardModal.selectMin = 1;
messageStore.checkCardModal.selectMax = 1;
messageStore.checkCardModal.onSubmit = "sendSelectChainResponse";
......@@ -83,35 +65,16 @@ export default (selectChain: MsgSelectChain, dispatch: AppDispatch) => {
messageStore.checkCardModal.cancelResponse = -1;
for (const chain of chains) {
// const tagName = CardZoneToChinese(chain.location.location);
// dispatch(
// fetchCheckCardMeta({
// tagName,
// option: {
// code: chain.code,
// location: chain.location,
// response: chain.response,
// effectDescCode: chain.effect_description,
// },
// })
// );
FIXME_fetchCheckCardMeta(chain.location.location, {
fetchCheckCardMeta(chain.location.location, {
code: chain.code,
location: chain.location,
response: chain.response,
effectDescCode: chain.effect_description,
});
}
// dispatch(
// fetchSelectHintMeta({
// selectHintData: 203,
// })
// );
FIXME_fetchSelectHintMeta({
fetchSelectHintMeta({
selectHintData: 203,
});
// dispatch(setCheckCardModalIsOpen(true));
messageStore.checkCardModal.isOpen = true;
break;
......
import { ygopro } from "@/api";
import { setCheckCounter } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { getCardByLocation, messageStore } from "@/valtioStores";
import { getCardByLocation, messageStore } from "@/stores";
type MsgSelectCounter = ygopro.StocGameMessage.MsgSelectCounter;
export default (selectCounter: MsgSelectCounter, dispatch: AppDispatch) => {
// dispatch(setCheckCounter(selectCounter.toObject()));
export default (selectCounter: MsgSelectCounter) => {
messageStore.checkCounterModal.counterType = selectCounter.counter_type;
messageStore.checkCounterModal.min = selectCounter.min;
messageStore.checkCounterModal.options = selectCounter.options!.map(
......
import { ygopro } from "@/api";
import { fetchStrings, ygopro } from "@/api";
import { CardMeta, fetchCard } from "@/api/cards";
import { fetchStrings } from "@/api/strings";
import { setYesNoModalIsOpen } from "@/reducers/duel/mod";
import { fetchYesNoMeta } from "@/reducers/duel/modal/mod";
import { AppDispatch } from "@/store";
import { getCardByLocation, messageStore } from "@/valtioStores";
import { messageStore } from "@/stores";
import { CardZoneToChinese } from "./util";
type MsgSelectEffectYn = ygopro.StocGameMessage.MsgSelectEffectYn;
// 这里改成了 async 不知道有没有影响
export default async (
selectEffectYn: MsgSelectEffectYn,
dispatch: AppDispatch
) => {
export default async (selectEffectYn: MsgSelectEffectYn) => {
const player = selectEffectYn.player;
const code = selectEffectYn.code;
const location = selectEffectYn.location;
......@@ -47,7 +40,6 @@ export default async (
// })
// );
// TODO: 国际化文案
dispatch(setYesNoModalIsOpen(true));
const desc = fetchStrings("!system", effect_description);
const meta = await fetchCard(code);
......
import { ActionCreatorWithPayload } from "@reduxjs/toolkit";
import { ygopro } from "@/api";
import { Interactivity, InteractType } from "@/reducers/duel/generic";
import {
addBanishedZoneIdleInteractivities,
addExtraDeckIdleInteractivities,
addGraveyardIdleInteractivities,
addHandsIdleInteractivity,
addMagicIdleInteractivities,
addMonsterIdleInteractivities,
clearAllIdleInteractivities,
setEnableBp,
setEnableEp,
} from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import {
clearAllIdleInteractivities as FIXME_clearAllIdleInteractivities,
clearAllIdleInteractivities as clearAllIdleInteractivities,
type Interactivity,
InteractType,
matStore,
} from "@/valtioStores";
} from "@/stores";
import MsgSelectIdleCmd = ygopro.StocGameMessage.MsgSelectIdleCmd;
export default (selectIdleCmd: MsgSelectIdleCmd, dispatch: AppDispatch) => {
export default (selectIdleCmd: MsgSelectIdleCmd) => {
const player = selectIdleCmd.player;
const cmds = selectIdleCmd.idle_cmds;
// 先清掉之前的互动性
// dispatch(clearAllIdleInteractivities(player));
FIXME_clearAllIdleInteractivities(player);
// const dispatcher = (
// idleData: MsgSelectIdleCmd.IdleCmd.IdleData,
// interactType: InteractType | undefined,
// actionCreator: ActionCreatorWithPayload<
// {
// player: number;
// sequence: number;
// interactivity: Interactivity<number>;
// },
// string
// >
// ) => {
// const cardInfo = idleData.card_info;
// if (interactType === InteractType.ACTIVATE) {
// // 发动效果会多一个字段
// dispatch(
// actionCreator({
// player,
// sequence: cardInfo.sequence,
// interactivity: {
// interactType,
// activateIndex: idleData.effect_description,
// response: idleData.response,
// },
// })
// );
// } else if (interactType) {
// dispatch(
// actionCreator({
// player,
// sequence: cardInfo.sequence,
// interactivity: { interactType, response: idleData.response },
// })
// );
// } else {
// console.log(`InteractType undefined`);
// }
// };
clearAllIdleInteractivities(player);
cmds.forEach((cmd) => {
const interactType = idleTypeToInteractType(cmd.idle_type);
......@@ -74,7 +21,7 @@ export default (selectIdleCmd: MsgSelectIdleCmd, dispatch: AppDispatch) => {
cmd.idle_datas.forEach((data) => {
const { location, sequence } = data.card_info;
// valtio代码从 ./selectBattleCmd.ts 复制过来的
// valtio: 代码从 ./selectBattleCmd.ts 复制过来的
if (interactType) {
const map: Partial<
Record<InteractType, undefined | Partial<Interactivity<number>>>
......@@ -93,48 +40,9 @@ export default (selectIdleCmd: MsgSelectIdleCmd, dispatch: AppDispatch) => {
} else {
console.warn(`Undefined InteractType`);
}
// switch (location) {
// case ygopro.CardZone.HAND: {
// dispatcher(data, interactType, addHandsIdleInteractivity);
// break;
// }
// case ygopro.CardZone.MZONE: {
// dispatcher(data, interactType, addMonsterIdleInteractivities);
// break;
// }
// case ygopro.CardZone.SZONE: {
// dispatcher(data, interactType, addMagicIdleInteractivities);
// break;
// }
// case ygopro.CardZone.GRAVE: {
// dispatcher(data, interactType, addGraveyardIdleInteractivities);
// break;
// }
// case ygopro.CardZone.REMOVED: {
// dispatcher(data, interactType, addBanishedZoneIdleInteractivities);
// break;
// }
// case ygopro.CardZone.EXTRA: {
// dispatcher(data, interactType, addExtraDeckIdleInteractivities);
// break;
// }
// default: {
// console.log(`Unhandled zone type: ${location}`);
// }
// }
});
});
// dispatch(setEnableBp(selectIdleCmd.enable_bp));
// dispatch(setEnableEp(selectIdleCmd.enable_ep));
matStore.phase.enableBp = selectIdleCmd.enable_bp;
matStore.phase.enableEp = selectIdleCmd.enable_ep;
};
......
import { fetchCard, getCardStr, ygopro } from "@/api";
import { setOptionModalIsOpen } from "@/reducers/duel/mod";
import { fetchOptionMeta } from "@/reducers/duel/modal/mod";
import { AppDispatch } from "@/store";
import MsgSelectOption = ygopro.StocGameMessage.MsgSelectOption;
import { messageStore } from "@/valtioStores";
import { messageStore } from "@/stores";
export default async (selectOption: MsgSelectOption, dispatch: AppDispatch) => {
export default async (selectOption: MsgSelectOption) => {
const player = selectOption.player;
const options = selectOption.options;
// for (let option of options) {
// dispatch(fetchOptionMeta(option));
// }
// dispatch(setOptionModalIsOpen(true));
await Promise.all(
options.map(async ({ code, response }) => {
const meta = await fetchCard(code >> 4);
......
import { ygopro } from "@/api";
import { AppDispatch } from "@/store";
import MsgSelectPlace = ygopro.StocGameMessage.MsgSelectPlace;
import {
addMagicPlaceInteractivities,
addMonsterPlaceInteractivities,
} from "@/reducers/duel/mod";
import { InteractType, valtioStore } from "@/valtioStores";
import { InteractType, matStore } from "@/stores";
const { matStore } = valtioStore;
type MsgSelectPlace = ygopro.StocGameMessage.MsgSelectPlace;
export default (selectPlace: MsgSelectPlace, dispatch: AppDispatch) => {
export default (selectPlace: MsgSelectPlace) => {
if (selectPlace.count != 1) {
console.warn(`Unhandled case: ${selectPlace}`);
return;
}
for (const place of selectPlace.places) {
switch (place.zone) {
case ygopro.CardZone.MZONE: {
// dispatch(
// addMonsterPlaceInteractivities([place.controler, place.sequence])
// );
matStore.monsters
.of(place.controler)
.setPlaceInteractivityType(
......@@ -31,9 +21,6 @@ export default (selectPlace: MsgSelectPlace, dispatch: AppDispatch) => {
break;
}
case ygopro.CardZone.SZONE: {
// dispatch(
// addMagicPlaceInteractivities([place.controler, place.sequence])
// );
matStore.magics
.of(place.controler)
.setPlaceInteractivityType(
......
import { ygopro } from "@/api";
import {
setPositionModalIsOpen,
setPositionModalPositions,
} from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { messageStore } from "@/valtioStores";
import { messageStore } from "@/stores";
type MsgSelectPosition = ygopro.StocGameMessage.MsgSelectPosition;
export default (selectPosition: MsgSelectPosition, dispatch: AppDispatch) => {
export default (selectPosition: MsgSelectPosition) => {
const player = selectPosition.player;
const positions = selectPosition.positions;
// dispatch(
// setPositionModalPositions(positions.map((position) => position.position))
// );
// dispatch(setPositionModalIsOpen(true));
messageStore.positionModal.positions = positions.map(
(position) => position.position
);
......
import { ygopro } from "@/api";
import {
setCheckCardModalV3AllLevel,
setCheckCardModalV3IsOpen,
setCheckCardModalV3MinMax,
setCheckCardModalV3OverFlow,
} from "@/reducers/duel/mod";
import { fetchCheckCardMetasV3 } from "@/reducers/duel/modal/checkCardModalV3Slice";
import { AppDispatch } from "@/store";
import {
fetchCheckCardMetasV3 as FIXME_fetchCheckCardMetasV3,
messageStore,
} from "@/valtioStores";
import { fetchCheckCardMetasV3, messageStore } from "@/stores";
type MsgSelectSum = ygopro.StocGameMessage.MsgSelectSum;
export default (selectSum: MsgSelectSum, dispatch: AppDispatch) => {
// dispatch(setCheckCardModalV3OverFlow(selectSum.overflow != 0));
export default (selectSum: MsgSelectSum) => {
messageStore.checkCardModalV3.overflow = selectSum.overflow != 0;
// dispatch(setCheckCardModalV3AllLevel(selectSum.level_sum));
messageStore.checkCardModalV3.allLevel = selectSum.level_sum;
// dispatch(
// setCheckCardModalV3MinMax({ min: selectSum.min, max: selectSum.max })
// );
messageStore.checkCardModalV3.selectMin = selectSum.min;
messageStore.checkCardModalV3.selectMax = selectSum.max;
// dispatch(
// fetchCheckCardMetasV3({
// mustSelect: true,
// options: selectSum.must_select_cards,
// })
// );
FIXME_fetchCheckCardMetasV3({
fetchCheckCardMetasV3({
mustSelect: true,
options: selectSum.must_select_cards,
});
// dispatch(
// fetchCheckCardMetasV3({
// mustSelect: false,
// options: selectSum.selectable_cards,
// })
// );
FIXME_fetchCheckCardMetasV3({
fetchCheckCardMetasV3({
mustSelect: false,
options: selectSum.selectable_cards,
});
// dispatch(setCheckCardModalV3IsOpen(true));
messageStore.checkCardModalV3.isOpen = true;
};
import { ygopro } from "@/api";
import {
setCheckCardModalV3AllLevel,
setCheckCardModalV3IsOpen,
setCheckCardModalV3MinMax,
setCheckCardModalV3OverFlow,
} from "@/reducers/duel/mod";
import { fetchCheckCardMetasV3 } from "@/reducers/duel/modal/checkCardModalV3Slice";
import { AppDispatch } from "@/store";
import {
fetchCheckCardMetasV3 as FIXME_fetchCheckCardMetasV3,
messageStore,
} from "@/valtioStores";
import { fetchCheckCardMetasV3, messageStore } from "@/stores";
type MsgSelectTribute = ygopro.StocGameMessage.MsgSelectTribute;
export default (selectTribute: MsgSelectTribute, dispatch: AppDispatch) => {
export default (selectTribute: MsgSelectTribute) => {
// TODO: 当玩家选择卡数大于`max`时,是否也合法?
// dispatch(setCheckCardModalV3OverFlow(true));
messageStore.checkCardModalV3.overflow = true;
// dispatch(setCheckCardModalV3AllLevel(0));
messageStore.checkCardModalV3.allLevel = 0;
// dispatch(
// setCheckCardModalV3MinMax({
// min: selectTribute.min,
// max: selectTribute.max,
// })
// );
messageStore.checkCardModalV3.selectMin = selectTribute.min;
messageStore.checkCardModalV3.selectMax = selectTribute.max;
// dispatch(
// fetchCheckCardMetasV3({
// mustSelect: false,
// options: selectTribute.selectable_cards.map((card) => {
// return {
// code: card.code,
// location: card.location,
// level1: card.level,
// level2: card.level,
// response: card.response,
// };
// }),
// })
// );
FIXME_fetchCheckCardMetasV3({
fetchCheckCardMetasV3({
mustSelect: false,
options: selectTribute.selectable_cards.map((card) => {
return {
......@@ -54,6 +22,6 @@ export default (selectTribute: MsgSelectTribute, dispatch: AppDispatch) => {
};
}),
});
// dispatch(setCheckCardModalV3IsOpen(true));
messageStore.checkCardModalV3.isOpen = true;
};
import { ygopro } from "@/api";
import {
setCheckCardModalV2CancelAble,
setCheckCardModalV2FinishAble,
setCheckCardModalV2IsOpen,
setCheckCardModalV2MinMax,
setCheckCardModalV2ResponseAble,
} from "@/reducers/duel/mod";
import { fetchCheckCardMetasV2 } from "@/reducers/duel/modal/checkCardModalV2Slice";
import { AppDispatch } from "@/store";
import {
fetchCheckCardMetasV2 as FIXME_fetchCheckCardMetasV2,
messageStore,
} from "@/valtioStores";
import { fetchCheckCardMetasV2, messageStore } from "@/stores";
type MsgSelectUnselectCard = ygopro.StocGameMessage.MsgSelectUnselectCard;
export default (
{
finishable,
cancelable,
min,
max,
selectable_cards: selectableCards,
selected_cards: selectedCards,
}: MsgSelectUnselectCard,
dispatch: AppDispatch
) => {
// dispatch(setCheckCardModalV2IsOpen(true));
// dispatch(setCheckCardModalV2FinishAble(finishable));
// dispatch(setCheckCardModalV2CancelAble(cancelable));
// dispatch(setCheckCardModalV2MinMax({ min, max }));
export default ({
finishable,
cancelable,
min,
max,
selectable_cards: selectableCards,
selected_cards: selectedCards,
}: MsgSelectUnselectCard) => {
messageStore.checkCardModalV2.isOpen = true;
messageStore.checkCardModalV2.finishAble = finishable;
messageStore.checkCardModalV2.cancelAble = cancelable;
messageStore.checkCardModalV2.selectMin = min;
messageStore.checkCardModalV2.selectMax = max;
// dispatch(
// fetchCheckCardMetasV2({
// selected: false,
// options: selectableCards.map((card) => {
// return {
// code: card.code,
// location: card.location,
// response: card.response,
// };
// }),
// })
// );
FIXME_fetchCheckCardMetasV2({
fetchCheckCardMetasV2({
selected: false,
options: selectableCards.map((card) => {
return {
......@@ -61,20 +28,7 @@ export default (
}),
});
// dispatch(
// fetchCheckCardMetasV2({
// selected: true,
// options: selectedCards.map((card) => {
// return {
// code: card.code,
// location: card.location,
// response: card.response,
// };
// }),
// })
// );
FIXME_fetchCheckCardMetasV2({
fetchCheckCardMetasV2({
selected: true,
options: selectedCards.map((card) => {
return {
......@@ -85,7 +39,5 @@ export default (
}),
});
// dispatch(setCheckCardModalV2ResponseAble(true));
messageStore.checkCardModalV2.responseable = true;
};
import { getStrings, ygopro } from "@/api";
import { setYesNoModalIsOpen } from "@/reducers/duel/mod";
import { fetchYesNoMetaWithEffecDesc } from "@/reducers/duel/modal/yesNoModalSlice";
import { AppDispatch } from "@/store";
import { messageStore } from "@/valtioStores";
import { messageStore } from "@/stores";
type MsgSelectYesNo = ygopro.StocGameMessage.MsgSelectYesNo;
export default async (selectYesNo: MsgSelectYesNo, dispatch: AppDispatch) => {
export default async (selectYesNo: MsgSelectYesNo) => {
const player = selectYesNo.player;
const effect_description = selectYesNo.effect_description;
// dispatch(fetchYesNoMetaWithEffecDesc(effect_description));
// dispatch(setYesNoModalIsOpen(true));
messageStore.yesNoModal.msg = await getStrings(effect_description);
messageStore.yesNoModal.isOpen = true;
};
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
import { fetchEsHintMeta } from "@/stores";
export default (_set: ygopro.StocGameMessage.MsgSet, dispatch: AppDispatch) => {
// dispatch(fetchEsHintMeta({ originMsg: 1601 }));
FIXME_fetchEsHintMeta({ originMsg: 1601 });
export default (_set: ygopro.StocGameMessage.MsgSet) => {
fetchEsHintMeta({ originMsg: 1601 });
};
import { ygopro } from "@/api";
import { updateHandsMeta } from "@/reducers/duel/handsSlice";
import { AppDispatch } from "@/store";
import MsgShuffleHand = ygopro.StocGameMessage.MsgShuffleHand;
import { matStore } from "@/stores";
import { matStore } from "@/valtioStores";
export default (shuffleHand: MsgShuffleHand, dispatch: AppDispatch) => {
// dispatch(
// updateHandsMeta({ controler: shuffleHand.player, codes: shuffleHand.hands })
// );
type MsgShuffleHand = ygopro.StocGameMessage.MsgShuffleHand;
export default (shuffleHand: MsgShuffleHand) => {
const { hands: codes, player: controller } = shuffleHand;
const metas = codes.map((code) => {
......
import { fetchCard, ygopro } from "@/api";
import { setSortCardModalIsOpen } from "@/reducers/duel/mod";
import { fetchSortCardMeta } from "@/reducers/duel/modal/sortCardModalSlice";
import { AppDispatch } from "@/store";
import MsgSortCard = ygopro.StocGameMessage.MsgSortCard;
import { messageStore } from "@/stores";
import { messageStore } from "@/valtioStores";
export default async (sortCard: MsgSortCard, dispatch: AppDispatch) => {
// for (const option of sortCard.options) {
// dispatch(fetchSortCardMeta(option.toObject()));
// }
// dispatch(setSortCardModalIsOpen(true));
type MsgSortCard = ygopro.StocGameMessage.MsgSortCard;
export default async (sortCard: MsgSortCard) => {
await Promise.all(
sortCard.options.map(async ({ code, response }) => {
const meta = await fetchCard(code!, true);
......
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
import { fetchEsHintMeta } from "@/stores";
export default (
_: ygopro.StocGameMessage.MsgSpSummoned,
dispatch: AppDispatch
) => {
// dispatch(fetchEsHintMeta({ originMsg: 1606 }));
FIXME_fetchEsHintMeta({ originMsg: 1606 });
export default (_: ygopro.StocGameMessage.MsgSpSummoned) => {
fetchEsHintMeta({ originMsg: 1606 });
};
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
export default (
spSummoning: ygopro.StocGameMessage.MsgSpSummoning,
dispatch: AppDispatch
) => {
// dispatch(
// fetchEsHintMeta({
// originMsg: "「[?]」特殊召唤宣言时",
// cardID: spSummoning.code,
// })
// );
FIXME_fetchEsHintMeta({
import { fetchEsHintMeta } from "@/stores";
export default (spSummoning: ygopro.StocGameMessage.MsgSpSummoning) => {
fetchEsHintMeta({
originMsg: "「[?]」特殊召唤宣言时",
cardID: spSummoning.code,
});
......
import { ygopro } from "@/api";
import {
infoInit,
initBanishedZone,
initDeck,
initGraveyard,
initHint,
initMagics,
initMonsters,
setSelfType,
} from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { valtioStore } from "@/valtioStores";
import { store } from "@/stores";
const { matStore } = valtioStore;
export default (
start: ygopro.StocGameMessage.MsgStart,
dispatch: AppDispatch
) => {
// dispatch(setSelfType(start.playerType));
const { matStore } = store;
export default (start: ygopro.StocGameMessage.MsgStart) => {
matStore.selfType = start.playerType;
matStore.initInfo.set(0, {
......@@ -33,44 +17,6 @@ export default (
extraSize: start.extraSize2,
});
// dispatch(
// infoInit([
// 0,
// {
// life: start.life1,
// deckSize: start.deckSize1,
// extraSize: start.extraSize1,
// },
// ])
// );
// dispatch(
// infoInit([
// 1,
// {
// life: start.life2,
// deckSize: start.deckSize2,
// extraSize: start.extraSize2,
// },
// ])
// );
// >>> 删除 >>>
// dispatch(initMonsters(0));
// dispatch(initMonsters(1));
// dispatch(initMagics(0));
// dispatch(initMagics(1));
// dispatch(initGraveyard(0));
// dispatch(initGraveyard(1));
// dispatch(initDeck({ player: 0, deskSize: start.deckSize1 }));
// dispatch(initDeck({ player: 1, deskSize: start.deckSize2 }));
// dispatch(initBanishedZone(0));
// dispatch(initBanishedZone(1));
// <<< 删除 <<<
// 上面的删除就可以了
matStore.monsters.of(0).forEach((x) => (x.location.controler = 0));
matStore.monsters.of(1).forEach((x) => (x.location.controler = 1));
matStore.magics.of(0).forEach((x) => (x.location.controler = 0));
......@@ -78,5 +24,4 @@ export default (
matStore.decks.of(0).add(Array(start.deckSize1).fill(0));
matStore.decks.of(1).add(Array(start.deckSize2).fill(0));
// dispatch(initHint()); // 直接删除
};
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
import { fetchEsHintMeta } from "@/stores";
export default (
_: ygopro.StocGameMessage.MsgSummoned,
dispatch: AppDispatch
) => {
// dispatch(fetchEsHintMeta({ originMsg: 1604 }));
FIXME_fetchEsHintMeta({ originMsg: 1604 });
export default (_: ygopro.StocGameMessage.MsgSummoned) => {
fetchEsHintMeta({ originMsg: 1604 });
};
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
import { fetchEsHintMeta } from "@/stores";
export default (
summoning: ygopro.StocGameMessage.MsgSummoning,
dispatch: AppDispatch
) => {
// dispatch(
// fetchEsHintMeta({
// originMsg: "「[?]」通常召唤宣言时",
// cardID: summoning.code,
// })
// );
FIXME_fetchEsHintMeta({
export default (summoning: ygopro.StocGameMessage.MsgSummoning) => {
fetchEsHintMeta({
originMsg: "「[?]」通常召唤宣言时",
cardID: summoning.code,
});
......
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
import { fetchEsHintMeta } from "@/stores";
export default (
_swap: ygopro.StocGameMessage.MsgSwap,
dispatch: AppDispatch
) => {
// dispatch(fetchEsHintMeta({ originMsg: 1602 }));
FIXME_fetchEsHintMeta({ originMsg: 1602 });
export default (_swap: ygopro.StocGameMessage.MsgSwap) => {
fetchEsHintMeta({ originMsg: 1602 });
};
import { ygopro } from "@/api";
import { sendTimeConfirm } from "@/api/ocgcore/ocgHelper";
import { updateTimeLimit } from "@/reducers/duel/mod";
import { store } from "@/store";
import { matStore } from "@/valtioStores";
import { sendTimeConfirm, ygopro } from "@/api";
import { matStore } from "@/stores";
export default function handleTimeLimit(timeLimit: ygopro.StocTimeLimit) {
// const dispatch = store.dispatch;
// dispatch(updateTimeLimit([timeLimit.player, timeLimit.left_time]));
matStore.timeLimits.set(timeLimit.player, timeLimit.left_time);
sendTimeConfirm();
}
import { ygopro } from "@/api";
import { useConfig } from "@/config";
import { setUnimplemented } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { matStore } from "@/valtioStores";
import { matStore } from "@/stores";
const NeosConfig = useConfig();
export default (
unimplemented: ygopro.StocGameMessage.MsgUnimplemented,
dispatch: AppDispatch
) => {
export default (unimplemented: ygopro.StocGameMessage.MsgUnimplemented) => {
if (!NeosConfig.unimplementedWhiteList.includes(unimplemented.command)) {
// dispatch(setUnimplemented(unimplemented.command));
matStore.unimplemented = unimplemented.command;
}
};
import { ygopro } from "@/api";
import { updateMonsterCounters } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { getCardByLocation } from "@/valtioStores";
import { getCardByLocation } from "@/stores";
type MsgUpdateCounter = ygopro.StocGameMessage.MsgUpdateCounter;
export default (updateCounter: MsgUpdateCounter, dispatch: AppDispatch) => {
// dispatch(updateMonsterCounters(updateCounter.toObject()));
export default (updateCounter: MsgUpdateCounter) => {
const { location, count, action_type: counterType } = updateCounter;
const target = getCardByLocation(location); // 不太确定这个后面能不能相应,我不好说
if (target) {
......
import { ygopro } from "@/api";
import { updateFieldData } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import MsgUpdateData = ygopro.StocGameMessage.MsgUpdateData;
import { matStore } from "@/valtioStores";
export default (updateData: MsgUpdateData, dispatch: AppDispatch) => {
// dispatch(updateFieldData(updateData.toObject()));
import { matStore } from "@/stores";
export default (updateData: MsgUpdateData) => {
const { player: controller, zone, actions } = updateData;
if (controller !== undefined && zone !== undefined && actions !== undefined) {
const field = matStore.in(zone).of(controller);
......
import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { updateHp } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import {
fetchEsHintMeta as FIXME_fetchEsHintMeta,
matStore,
} from "@/valtioStores";
import { fetchEsHintMeta, matStore } from "@/stores";
import MsgUpdateHp = ygopro.StocGameMessage.MsgUpdateHp;
export default (msgUpdateHp: MsgUpdateHp, dispatch: AppDispatch) => {
export default (msgUpdateHp: MsgUpdateHp) => {
if (msgUpdateHp.type_ == MsgUpdateHp.ActionType.DAMAGE) {
// dispatch(fetchEsHintMeta({ originMsg: "玩家收到伤害时" })); // TODO: i18n
FIXME_fetchEsHintMeta({ originMsg: "玩家收到伤害时" });
fetchEsHintMeta({ originMsg: "玩家收到伤害时" }); // TODO: i18n
matStore.initInfo.of(msgUpdateHp.player).life -= msgUpdateHp.value;
} else if (msgUpdateHp.type_ == MsgUpdateHp.ActionType.RECOVER) {
// dispatch(fetchEsHintMeta({ originMsg: "玩家生命值回复时" })); // TODO: i18n
FIXME_fetchEsHintMeta({ originMsg: "玩家生命值回复时" });
fetchEsHintMeta({ originMsg: "玩家生命值回复时" }); // TODO: i18n
matStore.initInfo.of(msgUpdateHp.player).life += msgUpdateHp.value;
}
dispatch(updateHp(msgUpdateHp)); // 可以删除了
};
import { ygopro } from "@/api";
import { clearAllIdleInteractivities, setWaiting } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import {
clearAllIdleInteractivities as FIXME_clearAllIdleInteractivities,
clearAllIdleInteractivities as clearAllIdleInteractivities,
matStore,
} from "@/valtioStores";
} from "@/stores";
export default (
_wait: ygopro.StocGameMessage.MsgWait,
dispatch: AppDispatch
) => {
// dispatch(clearAllIdleInteractivities(0));
// dispatch(clearAllIdleInteractivities(1));
// dispatch(setWaiting(true));
FIXME_clearAllIdleInteractivities(0);
FIXME_clearAllIdleInteractivities(1);
export default (_wait: ygopro.StocGameMessage.MsgWait) => {
clearAllIdleInteractivities(0);
clearAllIdleInteractivities(1);
matStore.waiting = true;
};
import { ygopro } from "@/api";
import { setResult } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { matStore } from "@/valtioStores";
import { matStore } from "@/stores";
export default (win: ygopro.StocGameMessage.MsgWin, dispatch: AppDispatch) => {
// dispatch(setResult(win.type_));
export default (win: ygopro.StocGameMessage.MsgWin) => {
matStore.result = win.type_;
};
import { ygopro } from "@/api";
import { player0DeckInfo, player1DeckInfo } from "@/reducers/playerSlice";
import { store } from "@/store";
import { playerStore } from "@/valtioStores";
import { playerStore } from "@/stores";
// FIXME: player0 不一定是当前玩家
export default function handleDeckCount(pb: ygopro.YgoStocMsg) {
// const dispath = store.dispatch;
const deckCount = pb.stoc_deck_count;
// dispath(
// player0DeckInfo({
// mainCnt: deckCount.meMain,
// extraCnt: deckCount.meExtra,
// sideCnt: deckCount.meSide,
// })
// );
playerStore.player0.deckInfo = {
mainCnt: deckCount.meMain,
extraCnt: deckCount.meExtra,
sideCnt: deckCount.meSide,
};
// dispath(
// player1DeckInfo({
// mainCnt: deckCount.opMain,
// extraCnt: deckCount.opExtra,
// sideCnt: deckCount.opSide,
// })
// );
playerStore.player1.deckInfo = {
mainCnt: deckCount.opMain,
extraCnt: deckCount.opExtra,
......
import { ygopro } from "@/api";
import { selectHandAble } from "@/reducers/moraSlice";
import { store } from "@/store";
import { moraStore } from "@/valtioStores";
import { moraStore } from "@/stores";
export default function handleSelectHand(_: ygopro.YgoStocMsg) {
// const dispatch = store.dispatch;
// dispatch(selectHandAble());
moraStore.selectHandAble = true;
}
import { ygopro } from "@/api";
import { selectTpAble } from "@/reducers/moraSlice";
import { store } from "@/store";
import { moraStore } from "@/valtioStores";
import { moraStore } from "@/stores";
export default function handleSelectTp(_: ygopro.YgoStocMsg) {
// const dispatch = store.dispatch;
// dispatch(selectTpAble());
moraStore.selectTpAble = true;
}
......@@ -2,7 +2,7 @@
* 长连接建立事件订阅处理逻辑
*
* */
import { sendJoinGame, sendPlayerInfo } from "@/api/ocgcore/ocgHelper";
import { sendJoinGame, sendPlayerInfo } from "@/api";
import { useConfig } from "@/config";
const NeosConfig = useConfig();
......
import { ygopro } from "@/api";
import { postChat } from "@/reducers/chatSlice";
import { store } from "@/store";
import { chatStore } from "@/valtioStores";
import { chatStore } from "@/stores";
export default function handleChat(pb: ygopro.YgoStocMsg) {
// const dispatch = store.dispatch;
const chat = pb.stoc_chat;
// dispatch(postChat(chat.msg));
chatStore.message = chat.msg;
}
import { ygopro } from "@/api";
import { duelStart } from "@/reducers/moraSlice";
import { store } from "@/store";
import { moraStore } from "@/valtioStores";
import { moraStore } from "@/stores";
export default function handleDuelStart(_pb: ygopro.YgoStocMsg) {
// const dispatch = store.dispatch;
// dispatch(duelStart());
moraStore.duelStart = true;
}
import { ygopro } from "@/api";
import {
observerIncrement,
player0Leave,
player0Update,
player1Leave,
player1Update,
} from "@/reducers/playerSlice";
import { store } from "@/store";
import { playerStore } from "@/valtioStores";
import { playerStore } from "@/stores";
const READY_STATE = "ready";
const NO_READY_STATE = "not ready";
export default function handleHsPlayerChange(pb: ygopro.YgoStocMsg) {
// const dispatch = store.dispatch;
const change = pb.stoc_hs_player_change;
if (change.pos > 1) {
......@@ -35,42 +26,25 @@ export default function handleHsPlayerChange(pb: ygopro.YgoStocMsg) {
// todo
// if (src === 0 && dst === 1) {
// setPlayer1(player0);
// setPlayer0({});
// } else if (src === 1 && dst === 0) {
// setPlayer0(player1);
// setPlayer1({});
// }
break;
}
case ygopro.StocHsPlayerChange.State.READY: {
// change.pos == 0
// ? dispatch(player0Update(READY_STATE))
// : dispatch(player1Update(READY_STATE));
playerStore[change.pos == 0 ? "player0" : "player1"].state =
READY_STATE;
break;
}
case ygopro.StocHsPlayerChange.State.NO_READY: {
// change.pos == 0
// ? dispatch(player0Update(NO_READY_STATE))
// : dispatch(player1Update(NO_READY_STATE));
playerStore[change.pos == 0 ? "player0" : "player1"].state =
NO_READY_STATE;
break;
}
case ygopro.StocHsPlayerChange.State.LEAVE: {
// change.pos == 0 ? dispatch(player0Leave) : dispatch(player1Leave);
playerStore[change.pos == 0 ? "player0" : "player1"] = {};
break;
}
case ygopro.StocHsPlayerChange.State.TO_OBSERVER: {
// change.pos == 0 ? dispatch(player0Leave) : dispatch(player1Leave);
// dispatch(observerIncrement());
playerStore[change.pos == 0 ? "player0" : "player1"] = {}; // todo: 有没有必要?
playerStore.observerCount += 1;
break;
......
import { ygopro } from "@/api";
import { player0Enter, player1Enter } from "@/reducers/playerSlice";
import { store } from "@/store";
import { playerStore } from "@/valtioStores";
import { playerStore } from "@/stores";
export default function handleHsPlayerEnter(pb: ygopro.YgoStocMsg) {
// const dispatch = store.dispatch;
const name = pb.stoc_hs_player_enter.name;
const pos = pb.stoc_hs_player_enter.pos;
if (pos > 1) {
console.log("Currently only supported 2v2 mode.");
} else {
// pos == 0 ? dispatch(player0Enter(name)) : dispatch(player1Enter(name));
playerStore[pos == 0 ? "player0" : "player1"].name = name;
}
}
import { ygopro } from "@/api";
import { observerChange } from "@/reducers/playerSlice";
import { store } from "@/store";
import { playerStore } from "@/valtioStores";
import { playerStore } from "@/stores";
export default function handleHsWatchChange(pb: ygopro.YgoStocMsg) {
// const dispatch = store.dispatch;
const count = pb.stoc_hs_watch_change.count;
// dispatch(observerChange(count));
playerStore.observerCount = count;
}
import { ygopro } from "@/api";
import { setJoined } from "@/reducers/joinSlice";
import { store } from "@/store";
import { joinStore } from "@/valtioStores";
import { joinStore } from "@/stores";
export default function handleJoinGame(pb: ygopro.YgoStocMsg) {
// const dispatch = store.dispatch;
const msg = pb.stoc_join_game;
// todo
// dispatch(setJoined());
joinStore.value = true;
}
import { ygopro } from "@/api";
import {
hostChange,
player0Update,
player1Update,
updateIsHost,
} from "@/reducers/playerSlice";
import { store } from "@/store";
import { playerStore } from "@/valtioStores";
import { playerStore } from "@/stores";
const NO_READY_STATE = "not ready";
export default function handleTypeChange(pb: ygopro.YgoStocMsg) {
// const dispatch = store.dispatch;
const selfType = pb.stoc_type_change.self_type;
const assertHost = pb.stoc_type_change.is_host;
// dispatch(updateIsHost(assertHost));
playerStore.isHost = assertHost;
if (assertHost) {
switch (selfType) {
case ygopro.StocTypeChange.SelfType.PLAYER1: {
// dispatch(hostChange(0));
// dispatch(player0Update(NO_READY_STATE));
playerStore.player0.isHost = true;
playerStore.player1.isHost = false;
playerStore.player0.state = NO_READY_STATE;
break;
}
case ygopro.StocTypeChange.SelfType.PLAYER2: {
// dispatch(hostChange(0));
// dispatch(player1Update(NO_READY_STATE));
playerStore.player0.isHost = false;
playerStore.player1.isHost = true;
playerStore.player1.state = NO_READY_STATE;
break;
}
default: {
......
/*
* 全局状态存储模块
* */
import { configureStore, Unsubscribe } from "@reduxjs/toolkit";
import chatReducer from "./reducers/chatSlice";
import duelReducer from "./reducers/duel/mod";
import joinedReducer from "./reducers/joinSlice";
import moraReducer from "./reducers/moraSlice";
import playerReducer from "./reducers/playerSlice";
export const store = configureStore({
reducer: {
join: joinedReducer,
chat: chatReducer,
player: playerReducer,
mora: moraReducer,
duel: duelReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: ["duel/updateHp", "duel/reloadField"],
},
}),
});
// Ref: https://github.com/reduxjs/redux/issues/303
export function observeStore<T>(
select: (state: RootState) => T,
onChange: (prev: T | null, cur: T) => void
): Unsubscribe {
let currentState: T | null = null;
const changeHook = () => {
const nextState = select(store.getState());
if (nextState !== currentState) {
onChange(currentState, nextState);
currentState = nextState;
}
};
const unsubscribe = store.subscribe(changeHook);
changeHook();
return unsubscribe;
}
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
......@@ -15,7 +15,7 @@ import { messageStore } from "./messageStore";
import { moraStore } from "./moraStore";
import { playerStore } from "./playerStore";
export const valtioStore = proxy({
export const store = proxy({
playerStore,
chatStore,
joinStore,
......@@ -24,4 +24,4 @@ export const valtioStore = proxy({
messageStore, // 决斗的信息,包括模态框
});
devtools(valtioStore, { name: "valtio store", enabled: true });
devtools(store, { name: "valtio store", enabled: true });
import { ygopro } from "@/api";
import { fetchCard, getCardStr } from "@/api/cards";
import { matStore, messageStore } from "@/valtioStores";
import { matStore, messageStore } from "@/stores";
function CardZoneToChinese(zone: ygopro.CardZone): string {
switch (zone) {
......
import type { ygopro } from "@/api";
import { DESCRIPTION_LIMIT, fetchStrings, getStrings } from "@/api";
import { fetchCard } from "@/api/cards";
import { DESCRIPTION_LIMIT, fetchStrings, getStrings } from "@/api/strings";
import { matStore } from "../store";
......
import { fetchCard } from "@/api";
import { matStore } from "@/valtioStores";
import { matStore } from "@/stores";
export const fetchOverlayMeta = async (
controller: number,
......
import type { ygopro } from "@/api";
import { matStore } from "@/valtioStores";
import { matStore } from "@/stores";
export const getCardByLocation = (location: ygopro.CardLocation) => {
return matStore.in(location.location).of(location.controler)[
......
......@@ -5,7 +5,6 @@ import { ygopro } from "@/api";
import { fetchCard } from "@/api/cards";
import type {
BothSide,
CardState,
DuelFieldState as ArrayCardState,
InitInfo,
......@@ -34,26 +33,15 @@ class CardArray extends Array<CardState> implements ArrayCardState {
counters: {},
idleInteractivities: [],
});
/** 内部输出一些注释,等稳定了再移除这个log */
private logInside(name: string, obj: Record<string, any>) {
console.warn("matStore", name, {
zone: ygopro.CardZone[this.zone],
controller: getWhom(this.getController()),
...obj,
});
}
// methods
remove(sequence: number) {
this.logInside("remove", { sequence });
this.splice(sequence, 1);
}
async insert(sequence: number, id: number) {
this.logInside("insert", { sequence, id });
const card = await this.genCard(this.getController(), id);
this.splice(sequence, 0, card);
}
async add(ids: number[]) {
this.logInside("add", { ids });
const cards = await Promise.all(
ids.map(async (id) => this.genCard(this.getController(), id))
);
......@@ -64,7 +52,6 @@ class CardArray extends Array<CardState> implements ArrayCardState {
id: number,
position?: ygopro.CardPosition
) {
this.logInside("setOccupant", { sequence, id, position });
const meta = await fetchCard(id);
const target = this[sequence];
target.occupant = meta;
......@@ -76,14 +63,12 @@ class CardArray extends Array<CardState> implements ArrayCardState {
sequence: number,
interactivity: CardState["idleInteractivities"][number]
) {
this.logInside("addIdleInteractivity", { sequence, interactivity });
this[sequence].idleInteractivities.push(interactivity);
}
clearIdleInteractivities() {
this.forEach((card) => (card.idleInteractivities = []));
}
setPlaceInteractivityType(sequence: number, interactType: InteractType) {
this.logInside("setPlaceInteractivityType", { sequence, interactType });
this[sequence].placeInteractivity = {
interactType: interactType,
response: {
......@@ -99,6 +84,7 @@ class CardArray extends Array<CardState> implements ArrayCardState {
}
const genDuelCardArray = (cardStates: CardState[], zone: ygopro.CardZone) => {
// 为什么不放在构造函数里面,是因为不想改造继承自Array的构造函数
const me = cloneDeep(new CardArray(...cardStates));
me.zone = zone;
me.getController = () => (matStore.selfType === 1 ? 0 : 1);
......@@ -239,12 +225,10 @@ export const matStore: MatState = proxy<MatState>({
isMe,
});
// 以后再来解决这些...
// @ts-ignore
// @ts-ignore 挂到全局,便于调试
window.matStore = matStore;
// 修改原型链,因为valtiol的proxy会把原型链改掉。这应该是valtio的一个bug...有空提issue去改
// 修改原型链,因为valtio的proxy会把原型链改掉。这应该是valtio的一个bug...有空提issue去改
(["me", "op"] as const).forEach((who) => {
(
[
......
import { matStore } from "@/valtioStores";
import { matStore } from "@/stores";
export const clearAllIdleInteractivities = (controller: number) => {
matStore.banishedZones.of(controller).clearIdleInteractivities();
......
import { matStore } from "@/valtioStores";
import { ygopro } from "@/api";
import { matStore } from "@/stores";
/** 清空所有place互动性,也可以删除某一个zone的互动性。zone为空则为清除所有。 */
export const clearAllPlaceInteradtivities = (
......
import { fetchCard, type ygopro } from "@/api";
import { getCardByLocation, messageStore } from "@/valtioStores";
import { getCardByLocation, messageStore } from "@/stores";
export const fetchCheckCardMetasV2 = async ({
selected,
......
import { fetchCard, type ygopro } from "@/api";
import { getCardByLocation, messageStore } from "@/valtioStores";
import { getCardByLocation, messageStore } from "@/stores";
export const fetchCheckCardMetasV3 = async ({
mustSelect,
......
export * from "./clearAllIdleInteractivities";
export * from "./clearAllPlaceInteradtivities";
export * from "./fetchCheckCardMetasV2";
export * from "./fetchCheckCardMetasV3";
export * from "./clearAllPlaceInteradtivities";
......@@ -2,7 +2,6 @@ import * as BABYLON from "@babylonjs/core";
import { Row } from "antd";
import React from "react";
import { Engine, Scene } from "react-babylonjs";
import { Provider, ReactReduxContext } from "react-redux";
import { useConfig } from "@/config";
......@@ -75,28 +74,22 @@ const NeosSider = () => (
);
const NeosCanvas = () => (
<ReactReduxContext.Consumer>
{({ store }) => (
<Engine antialias adaptToDeviceRatio canvasId="babylonJS">
<Scene>
<Provider store={store}>
<Camera />
<Light />
<Hands />
<Monsters />
<Magics />
<Field />
<CommonDeck />
<ExtraDeck />
<Graveyard />
<BanishedZone />
<Field />
<Ground />
</Provider>
</Scene>
</Engine>
)}
</ReactReduxContext.Consumer>
<Engine antialias adaptToDeviceRatio canvasId="babylonJS">
<Scene>
<Camera />
<Light />
<Hands />
<Monsters />
<Magics />
<Field />
<CommonDeck />
<ExtraDeck />
<Graveyard />
<BanishedZone />
<Field />
<Ground />
</Scene>
</Engine>
);
const Camera = () => (
......
import { Alert as AntdAlert } from "antd";
import React from "react";
import { useNavigate } from "react-router-dom";
import { sendSurrender } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import { selectUnimplemented } from "@/reducers/duel/mod";
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
export const Alert = () => {
// const unimplemented = useAppSelector(selectUnimplemented);
import { sendSurrender } from "@/api";
import { matStore } from "@/stores";
export const Alert = () => {
const matSnap = useSnapshot(matStore);
const unimplemented = matSnap.unimplemented;
......
import { Button, Drawer, List } from "antd";
import React from "react";
import { useSnapshot } from "valtio";
import { sendSelectIdleCmdResponse } from "@/api/ocgcore/ocgHelper";
import { sendSelectIdleCmdResponse } from "@/api";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
clearAllIdleInteractivities,
setCardListModalIsOpen,
} from "@/reducers/duel/mod";
import {
selectCardListModalInfo,
selectCardListModalIsOpen,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import {
clearAllIdleInteractivities as clearAllIdleInteractivities,
messageStore,
matStore,
clearAllIdleInteractivities as FIXME_clearAllIdleInteractivities,
} from "@/valtioStores";
import { useSnapshot } from "valtio";
} from "@/stores";
const NeosConfig = useConfig();
......@@ -28,16 +16,11 @@ const CARD_WIDTH = 100;
const { cardListModal } = messageStore;
export const CardListModal = () => {
// const dispatch = store.dispatch;
// const isOpen = useAppSelector(selectCardListModalIsOpen);
// const list = useAppSelector(selectCardListModalInfo);
const snapCardListModal = useSnapshot(cardListModal);
const isOpen = snapCardListModal.isOpen;
const list = snapCardListModal.list as typeof cardListModal.list;
const handleOkOrCancel = () => {
// dispatch(setCardListModalIsOpen(false));
cardListModal.isOpen = false;
};
......@@ -53,13 +36,9 @@ export const CardListModal = () => {
key={idx}
onClick={() => {
sendSelectIdleCmdResponse(interactivy.response);
// dispatch(setCardListModalIsOpen(false));
// dispatch(clearAllIdleInteractivities(0));
// dispatch(clearAllIdleInteractivities(1));
cardListModal.isOpen = false;
FIXME_clearAllIdleInteractivities(0);
FIXME_clearAllIdleInteractivities(1);
clearAllIdleInteractivities(0);
clearAllIdleInteractivities(1);
}}
>
{interactivy.desc}
......
......@@ -3,22 +3,14 @@ import { Button, Card, Col, Modal, Row } from "antd";
import { ReactComponent as BattleSvg } from "neos-assets/battle-axe.svg";
import { ReactComponent as DefenceSvg } from "neos-assets/checked-shield.svg";
import React from "react";
import { useSnapshot } from "valtio";
import { sendSelectIdleCmdResponse } from "@/api/ocgcore/ocgHelper";
import { fetchStrings } from "@/api/strings";
import { fetchStrings, sendSelectIdleCmdResponse } from "@/api";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
clearAllIdleInteractivities,
setCardModalIsOpen,
} from "@/reducers/duel/mod";
import {
selectCardModalCounters,
selectCardModalInteractivies,
selectCardModalIsOpen,
selectCardModalMeta,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
clearAllIdleInteractivities as clearAllIdleInteractivities,
messageStore,
} from "@/stores";
import {
Attribute2StringCodeMap,
......@@ -27,12 +19,6 @@ import {
Type2StringCodeMap,
} from "../../../common";
import {
messageStore,
clearAllIdleInteractivities as FIXME_clearAllIdleInteractivities,
} from "@/valtioStores";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
const { Meta } = Card;
const CARD_WIDTH = 240;
......@@ -64,11 +50,9 @@ export const CardModal = () => {
const imgUrl = meta?.id
? `${NeosConfig.cardImgUrl}/${meta.id}.jpg`
: undefined;
// const interactivies = useAppSelector(selectCardModalInteractivies);
const interactivies = snapCardModal.interactivies;
const handleOkOrCancel = () => {
// dispatch(setCardModalIsOpen(false));
cardModal.isOpen = false;
};
......@@ -95,12 +79,9 @@ export const CardModal = () => {
key={idx}
onClick={() => {
sendSelectIdleCmdResponse(interactive.response);
// dispatch(setCardModalIsOpen(false));
// dispatch(clearAllIdleInteractivities(0));
// dispatch(clearAllIdleInteractivities(1));
cardModal.isOpen = false;
FIXME_clearAllIdleInteractivities(0);
FIXME_clearAllIdleInteractivities(1);
clearAllIdleInteractivities(0);
clearAllIdleInteractivities(1);
}}
>
{interactive.desc}
......
......@@ -2,48 +2,20 @@ import { ThunderboltOutlined } from "@ant-design/icons";
import { CheckCard, CheckCardProps } from "@ant-design/pro-components";
import { Button, Col, Popover, Row } from "antd";
import React, { useState } from "react";
import { useSnapshot } from "valtio";
import {
sendSelectCardResponse,
sendSelectChainResponse,
} from "@/api/ocgcore/ocgHelper";
import { sendSelectCardResponse, sendSelectChainResponse } from "@/api";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import {
resetCheckCardModal,
setCheckCardModalIsOpen,
} from "@/reducers/duel/mod";
import {
selectCheckCardModalCacnelResponse,
selectCheckCardModalCancelAble,
selectCheckCardModalIsOpen,
selectCheckCardModalMinMax,
selectCheckCardModalOnSubmit,
selectCheckCardModalTags,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import { matStore, messageStore } from "@/stores";
import { DragModal } from "./DragModal";
import { messageStore, matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
const { checkCardModal } = messageStore;
export const CheckCardModal = () => {
const snapCheckCardModal = useSnapshot(checkCardModal);
// const dispatch = store.dispatch;
// const isOpen = useAppSelector(selectCheckCardModalIsOpen);
// const { min, max } = useAppSelector(selectCheckCardModalMinMax);
// const tabs = useAppSelector(selectCheckCardModalTags);
// const onSubmit = useAppSelector(selectCheckCardModalOnSubmit);
// const cancelAble = useAppSelector(selectCheckCardModalCancelAble);
// const cancelResponse = useAppSelector(selectCheckCardModalCacnelResponse);
const isOpen = snapCheckCardModal.isOpen;
const min = snapCheckCardModal.selectMin ?? 0;
const max = snapCheckCardModal.selectMax ?? 10;
......@@ -54,7 +26,6 @@ export const CheckCardModal = () => {
const [response, setResponse] = useState<number[]>([]);
const defaultValue: number[] = [];
// const hint = useAppSelector(selectHint);
const hint = useSnapshot(matStore.hint);
const preHintMsg = hint?.esHint || "";
const selectHintMsg = hint?.esSelectHint || "请选择卡片";
......@@ -78,13 +49,7 @@ export const CheckCardModal = () => {
}
};
const FIXME_resetCheckCardModal = () => {
// state.modalState.checkCardModal.isOpen = false;
// state.modalState.checkCardModal.selectMin = undefined;
// state.modalState.checkCardModal.selectMax = undefined;
// state.modalState.checkCardModal.cancelAble = false;
// state.modalState.checkCardModal.cancelResponse = undefined;
// state.modalState.checkCardModal.tags = [];
const resetCheckCardModal = () => {
checkCardModal.isOpen = false;
checkCardModal.selectMin = undefined;
checkCardModal.selectMax = undefined;
......@@ -104,10 +69,8 @@ export const CheckCardModal = () => {
disabled={response.length < min || response.length > max}
onClick={() => {
sendResponseHandler(onSubmit, response);
// dispatch(setCheckCardModalIsOpen(false));
// dispatch(resetCheckCardModal());
checkCardModal.isOpen = false;
FIXME_resetCheckCardModal();
resetCheckCardModal();
}}
onFocus={() => {}}
onBlur={() => {}}
......@@ -120,10 +83,8 @@ export const CheckCardModal = () => {
if (cancelResponse) {
sendResponseHandler(onSubmit, [cancelResponse]);
}
// dispatch(setCheckCardModalIsOpen(false));
// dispatch(resetCheckCardModal());
checkCardModal.isOpen = false;
FIXME_resetCheckCardModal();
resetCheckCardModal();
}}
onFocus={() => {}}
onBlur={() => {}}
......
import { CheckCard } from "@ant-design/pro-components";
import { Button, Card, Col, Row } from "antd";
import React from "react";
import { useSnapshot } from "valtio";
import { sendSelectUnselectCardResponse } from "@/api/ocgcore/ocgHelper";
import { sendSelectUnselectCardResponse } from "@/api";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import {
resetCheckCardModalV2,
setCheckCardModalV2IsOpen,
setCheckCardModalV2ResponseAble,
} from "@/reducers/duel/mod";
import {
selectCheckCardModalV2CancelAble,
selectCheckCardModalV2FinishAble,
selectCheckCardModalV2IsOpen,
selectCheckCardModalV2MinMax,
selectCheckCardModalV2ResponseAble,
selectCheckCardModalV2SelectAbleOptions,
selectCheckCardModalV2SelectedOptions,
} from "@/reducers/duel/modal/checkCardModalV2Slice";
import { store } from "@/store";
import { matStore, messageStore } from "@/stores";
import { DragModal } from "./DragModal";
import { messageStore, matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const { checkCardModalV2 } = messageStore;
const NeosConfig = useConfig();
export const CheckCardModalV2 = () => {
const snapCheckCardModalV2 = useSnapshot(checkCardModalV2);
// const dispatch = store.dispatch;
// const isOpen = useAppSelector(selectCheckCardModalV2IsOpen);
// const { min, max } = useAppSelector(selectCheckCardModalV2MinMax);
// const cancelable = useAppSelector(selectCheckCardModalV2CancelAble);
// const finishable = useAppSelector(selectCheckCardModalV2FinishAble);
// const selectableOptions = useAppSelector(
// selectCheckCardModalV2SelectAbleOptions
// );
// const selectedOptions = useAppSelector(selectCheckCardModalV2SelectedOptions);
// const responseable = useAppSelector(selectCheckCardModalV2ResponseAble);
// const hint = useAppSelector(selectHint);
const isOpen = snapCheckCardModalV2.isOpen;
const min = snapCheckCardModalV2.selectMin ?? 0;
......@@ -57,13 +28,7 @@ export const CheckCardModalV2 = () => {
const preHintMsg = hint?.esHint || "";
const selectHintMsg = hint?.esSelectHint || "请选择卡片";
const FIXME_resetCheckCardModalV2 = () => {
// modalState.isOpen = false;
// modalState.finishAble = false;
// modalState.cancelAble = false;
// modalState.responseable = false;
// modalState.selectableOptions = [];
// modalState.selectedOptions = [];
const resetCheckCardModalV2 = () => {
checkCardModalV2.isOpen = false;
checkCardModalV2.finishAble = false;
checkCardModalV2.cancelAble = false;
......@@ -73,13 +38,10 @@ export const CheckCardModalV2 = () => {
};
const onFinishOrCancel = () => {
sendSelectUnselectCardResponse({ cancel_or_finish: true });
// dispatch(setCheckCardModalV2IsOpen(false));
// dispatch(resetCheckCardModalV2());
// dispatch(setCheckCardModalV2ResponseAble(false));
checkCardModalV2.isOpen = false;
checkCardModalV2.responseable = false;
FIXME_resetCheckCardModalV2();
resetCheckCardModalV2();
};
return (
......@@ -110,10 +72,8 @@ export const CheckCardModalV2 = () => {
size="small"
onChange={(value) => {
if (responseable) {
// dispatch(setCheckCardModalV2IsOpen(false));
// @ts-ignore
sendSelectUnselectCardResponse({ selected_ptr: value });
// dispatch(setCheckCardModalV2ResponseAble(false));
checkCardModalV2.isOpen = false;
checkCardModalV2.responseable = false;
}
......
import { CheckCard } from "@ant-design/pro-components";
import { Button, Card, Col, Row } from "antd";
import React, { useState } from "react";
import { useSnapshot } from "valtio";
import { sendSelectCardResponse } from "@/api/ocgcore/ocgHelper";
import { sendSelectCardResponse } from "@/api";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import {
resetCheckCardModalV3,
setCheckCardModalV3IsOpen,
setCheckCardModalV3ResponseAble,
} from "@/reducers/duel/mod";
import { selectCheckCardModalV3 } from "@/reducers/duel/modal/checkCardModalV3Slice";
import { store } from "@/store";
import { matStore, messageStore } from "@/stores";
import { DragModal } from "./DragModal";
import { messageStore, matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
const { checkCardModalV3 } = messageStore;
......@@ -26,17 +16,6 @@ const { checkCardModalV3 } = messageStore;
export const CheckCardModalV3 = () => {
const snapCheckCardModalV3 = useSnapshot(checkCardModalV3);
// const dispatch = store.dispatch;
// const state = useAppSelector(selectCheckCardModalV3);
// const isOpen = state.isOpen;
// const min = state.selectMin || 0;
// const max = state.selectMax || 0;
// const mustSelectOptions = state.mustSelectList;
// const selectAbleOptions = state.selectAbleList;
// const overflow = state.overflow;
// const LevelSum = state.allLevel;
const isOpen = snapCheckCardModalV3.isOpen;
const min = snapCheckCardModalV3.selectMin || 0;
const max = snapCheckCardModalV3.selectMax || 0;
......@@ -54,7 +33,6 @@ export const CheckCardModalV3 = () => {
.concat(selectedOptions)
.map((option) => option.level2)
.reduce((sum, current) => sum + current, 0);
// const hint = useAppSelector(selectHint);
const hint = useSnapshot(matStore.hint);
const preHintMsg = hint?.esHint || "";
const selectHintMsg = hint?.esSelectHint || "请选择卡片";
......@@ -69,24 +47,12 @@ export const CheckCardModalV3 = () => {
sendSelectCardResponse(
mustSelectOptions.concat(selectedOptions).map((option) => option.response)
);
// dispatch(setCheckCardModalV3IsOpen(false));
// dispatch(resetCheckCardModalV3());
// dispatch(setCheckCardModalV3ResponseAble(false));
checkCardModalV3.isOpen = false;
checkCardModalV3.responseable = false;
checkCardModalV3.overflow = false;
checkCardModalV3.allLevel = 0;
checkCardModalV3.mustSelectList = [];
checkCardModalV3.selectAbleList = [];
// 下面就是resetCheckCardModalV3的内容
// modalState.isOpen = false;
// modalState.overflow = false;
// modalState.allLevel = 0;
// modalState.responseable = undefined;
// modalState.mustSelectList = [];
// modalState.selectAbleList = [];
};
return (
......
import { Button, Card, Col, InputNumber, Row } from "antd";
import React, { useState } from "react";
import { useSnapshot } from "valtio";
import { sendSelectCounterResponse } from "@/api/ocgcore/ocgHelper";
import { fetchStrings } from "@/api/strings";
import { fetchStrings, sendSelectCounterResponse } from "@/api";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { clearCheckCounter } from "@/reducers/duel/mod";
import { selectCheckCounterModal } from "@/reducers/duel/modal/checkCounterModalSlice";
import { store } from "@/store";
import { messageStore } from "@/stores";
import { DragModal } from "./DragModal";
import { messageStore, matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const { checkCounterModal } = messageStore;
const NeosConfig = useConfig();
export const CheckCounterModal = () => {
const snapCheckCounterModal = useSnapshot(checkCounterModal);
const snapMat = useSnapshot(matStore);
// const dispatch = store.dispatch;
// const state = useAppSelector(selectCheckCounterModal);
// const isOpen = state.isOpen;
// const counterName = fetchStrings("!counter", `0x${state.counterType!}`);
// const min = state.min || 0;
// const options = state.options;
const isOpen = snapCheckCounterModal.isOpen;
const min = snapCheckCounterModal.min || 0;
......@@ -42,7 +28,6 @@ export const CheckCounterModal = () => {
const onFinish = () => {
sendSelectCounterResponse(selected);
// dispatch(clearCheckCounter());
messageStore.checkCounterModal.isOpen = false;
messageStore.checkCounterModal.min = undefined;
messageStore.checkCounterModal.counterType = undefined;
......
import { notification } from "antd";
import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useSnapshot } from "valtio";
import { ygopro } from "@/api";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import { selectDuelResult, selectWaiting } from "@/reducers/duel/mod";
import { selectCurrentPhase } from "@/reducers/duel/phaseSlice";
import MsgWin = ygopro.StocGameMessage.MsgWin;
import { useConfig } from "@/config";
import { matStore } from "@/stores";
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const MsgWin = ygopro.StocGameMessage.MsgWin;
const NeosConfig = useConfig();
export const HintNotification = () => {
// const hint = useAppSelector(selectHint);
const hint = useSnapshot(matStore.hint);
const currentPhase = useAppSelector(selectCurrentPhase);
const waiting = useAppSelector(selectWaiting);
const result = useAppSelector(selectDuelResult);
const hintState = matStore.hint;
const hintSnap = useSnapshot(matStore.hint);
const currentPhase = matStore.phase.currentPhase;
const waiting = matStore.waiting;
const result = matStore.result;
const navigate = useNavigate();
const [api, contextHolder] = notification.useNotification({
maxCount: NeosConfig.ui.hint.maxCount,
});
useEffect(() => {
if (hint && hint.msg) {
if (hintState && hintState.msg) {
api.info({
message: `${hint.msg}`,
message: `${hintState.msg}`,
placement: "bottom",
});
}
}, [hint?.msg]);
}, [hintSnap?.msg]);
useEffect(() => {
if (currentPhase) {
......
import { CheckCard } from "@ant-design/pro-components";
import { Button } from "antd";
import React, { useState } from "react";
import { useSnapshot } from "valtio";
import { sendSelectOptionResponse } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import { resetOptionModal, setOptionModalIsOpen } from "@/reducers/duel/mod";
import {
selectOptionModalIsOpen,
selectOptionModalOptions,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import { sendSelectOptionResponse } from "@/api";
import { messageStore } from "@/stores";
import { DragModal } from "./DragModal";
import { messageStore, matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const { optionModal } = messageStore;
export const OptionModal = () => {
// const dispatch = store.dispatch;
// const isOpen = useAppSelector(selectOptionModalIsOpen);
// const options = useAppSelector(selectOptionModalOptions);
const snapOptionModal = useSnapshot(optionModal);
const isOpen = snapOptionModal.isOpen;
......@@ -40,8 +29,6 @@ export const OptionModal = () => {
onClick={() => {
if (selected !== undefined) {
sendSelectOptionResponse(selected);
// dispatch(setOptionModalIsOpen(false));
// dispatch(resetOptionModal());
optionModal.isOpen = false;
optionModal.options = [];
}
......
......@@ -5,33 +5,17 @@ import { ReactComponent as EpSvg } from "neos-assets/power-button.svg";
import { ReactComponent as Main2Svg } from "neos-assets/sword-in-stone.svg";
import { ReactComponent as SurrenderSvg } from "neos-assets/truce.svg";
import React, { useState } from "react";
import { useSnapshot } from "valtio";
import {
sendSelectBattleCmdResponse,
sendSelectIdleCmdResponse,
sendSurrender,
} from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import {
clearAllIdleInteractivities,
setEnableBp,
setEnableEp,
setEnableM2,
} from "@/reducers/duel/mod";
} from "@/api";
import {
selectCurrentPhase,
selectEnableBp,
selectEnableEp,
selectEnableM2,
} from "@/reducers/duel/phaseSlice";
import { store } from "@/store";
import {
messageStore,
clearAllIdleInteractivities as clearAllIdleInteractivities,
matStore,
clearAllIdleInteractivities as FIXME_clearAllIdleInteractivities,
} from "@/valtioStores";
import { useSnapshot } from "valtio";
} from "@/stores";
const IconSize = "150%";
const SpaceSize = 16;
......@@ -57,12 +41,6 @@ const PhaseButton = (props: {
const { phase } = matStore;
export const Phase = () => {
// const dispatch = store.dispatch;
// const enableBp = useAppSelector(selectEnableBp);
// const enableM2 = useAppSelector(selectEnableM2);
// const enableEp = useAppSelector(selectEnableEp);
// const currentPhase = useAppSelector(selectCurrentPhase);
const snapPhase = useSnapshot(phase);
const enableBp = snapPhase.enableBp;
const enableM2 = snapPhase.enableM2;
......@@ -81,33 +59,21 @@ export const Phase = () => {
: 7;
const onBp = () => {
// dispatch(clearAllIdleInteractivities(0));
// dispatch(clearAllIdleInteractivities(0));
sendSelectIdleCmdResponse(6);
// dispatch(setEnableBp(false));
FIXME_clearAllIdleInteractivities(0); // 为什么要clear两次?
FIXME_clearAllIdleInteractivities(0);
clearAllIdleInteractivities(0); // 为什么要clear两次?
clearAllIdleInteractivities(0);
phase.enableBp = false;
};
const onM2 = () => {
// dispatch(clearAllIdleInteractivities(0));
// dispatch(clearAllIdleInteractivities(0));
sendSelectBattleCmdResponse(2);
// dispatch(setEnableM2(false));
FIXME_clearAllIdleInteractivities(0);
FIXME_clearAllIdleInteractivities(0);
clearAllIdleInteractivities(0);
clearAllIdleInteractivities(0);
phase.enableM2 = false;
};
const onEp = () => {
// dispatch(clearAllIdleInteractivities(0));
// dispatch(clearAllIdleInteractivities(0));
sendSelectIdleCmdResponse(response);
// dispatch(setEnableEp(false));
FIXME_clearAllIdleInteractivities(0);
FIXME_clearAllIdleInteractivities(0);
clearAllIdleInteractivities(0);
clearAllIdleInteractivities(0);
phase.enableEp = false;
};
const onSurrender = () => {
......
import { CheckCard } from "@ant-design/pro-components";
import { Button } from "antd";
import React, { useState } from "react";
import { useSnapshot } from "valtio";
import { ygopro } from "@/api";
import { sendSelectPositionResponse } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import {
resetPositionModal,
setPositionModalIsOpen,
} from "@/reducers/duel/mod";
import {
selectPositionModalIsOpen,
selectPositionModalPositions,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import { sendSelectPositionResponse, ygopro } from "@/api";
import { messageStore } from "@/stores";
import { DragModal } from "./DragModal";
import { messageStore, matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const { positionModal } = messageStore;
export const PositionModal = () => {
// const dispatch = store.dispatch;
const snapPositionModal = useSnapshot(positionModal);
// const isOpen = useAppSelector(selectPositionModalIsOpen);
// const positions = useAppSelector(selectPositionModalPositions);
const isOpen = snapPositionModal.isOpen;
const positions = snapPositionModal.positions;
......@@ -45,8 +30,6 @@ export const PositionModal = () => {
onClick={() => {
if (selected !== undefined) {
sendSelectPositionResponse(selected);
// dispatch(setPositionModalIsOpen(false));
// dispatch(resetPositionModal());
positionModal.isOpen = false;
positionModal.positions = [];
}
......
......@@ -2,7 +2,7 @@ import { SendOutlined } from "@ant-design/icons";
import { Button, Col, Input, Row } from "antd";
import React, { useState } from "react";
import { sendChat } from "@/api/ocgcore/ocgHelper";
import { sendChat } from "@/api";
export const SendBox = () => {
const [content, setContent] = useState("");
......
......@@ -17,29 +17,19 @@ import {
import { CSS } from "@dnd-kit/utilities";
import { Button, Card, Modal } from "antd";
import React, { useEffect, useState } from "react";
import { useSnapshot } from "valtio";
import { sendSortCardResponse } from "@/api";
import { CardMeta } from "@/api/cards";
import { sendSortCardResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { resetSortCardModal } from "@/reducers/duel/mod";
import { selectSortCardModal } from "@/reducers/duel/modal/sortCardModalSlice";
import { store } from "@/store";
import { messageStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
import { messageStore } from "@/stores";
const NeosConfig = useConfig();
const { sortCardModal } = messageStore;
export const SortCardModal = () => {
// const dispatch = store.dispatch;
const snapSortCardModal = useSnapshot(sortCardModal);
// const state = useAppSelector(selectSortCardModal);
// const isOpen = state.isOpen;
// const options = state.options;
const isOpen = snapSortCardModal.isOpen;
const options = snapSortCardModal.options;
const [items, setItems] = useState(options);
......@@ -52,7 +42,6 @@ export const SortCardModal = () => {
const onFinish = () => {
sendSortCardResponse(items.map((item) => item.response));
// dispatch(resetSortCardModal());
sortCardModal.isOpen = false;
sortCardModal.options = [];
};
......
......@@ -4,12 +4,6 @@ import { Avatar } from "antd";
import React from "react";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
selectMeInitInfo,
selectOpInitInfo,
} from "@/reducers/duel/initInfoSlice";
import { selectWaiting } from "@/reducers/duel/mod";
const NeosConfig = useConfig();
......@@ -18,14 +12,11 @@ const avatarSize = 40;
const ME_VALUE = "myself";
const OP_VALUE = "opponent";
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
export const PlayerStatus = () => {
// const meInfo = useAppSelector(selectMeInitInfo);
// const opInfo = useAppSelector(selectOpInitInfo);
// const waiting = useAppSelector(selectWaiting) || false;
import { matStore } from "@/stores";
export const PlayerStatus = () => {
const meInfo = useSnapshot(matStore.initInfo.me);
const opInfo = useSnapshot(matStore.initInfo.op);
const waiting = useSnapshot(matStore).waiting;
......
import { MessageOutlined } from "@ant-design/icons";
import { Timeline, TimelineItemProps } from "antd";
import React, { useContext, useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
import { useSnapshot } from "valtio";
import { useAppSelector } from "@/hook";
import { selectChat } from "@/reducers/chatSlice";
import { chatStore } from "@/valtioStores";
import { chatStore } from "@/stores";
export const DuelTimeLine = () => {
const [items, setItems] = useState<TimelineItemProps[]>([]);
// const chat = useAppSelector(selectChat);
const stateChat = chatStore;
const snapChat = useSnapshot(stateChat);
......
import { Button } from "antd";
import React from "react";
import { useSnapshot } from "valtio";
import { sendSelectEffectYnResponse } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import { setYesNoModalIsOpen } from "@/reducers/duel/mod";
import {
selectYesNoModalIsOpen,
selectYesNOModalMsg,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import { sendSelectEffectYnResponse } from "@/api";
import { matStore, messageStore } from "@/stores";
import { DragModal } from "./DragModal";
import { messageStore, matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const { yesNoModal } = messageStore;
export const YesNoModal = () => {
// const dispatch = store.dispatch;
// const isOpen = useAppSelector(selectYesNoModalIsOpen);
// const msg = useAppSelector(selectYesNOModalMsg);
// const hint = useAppSelector(selectHint);
const snapYesNoModal = useSnapshot(yesNoModal);
const isOpen = snapYesNoModal.isOpen;
const msg = snapYesNoModal.msg;
......
import * as BABYLON from "@babylonjs/core";
import { useSnapshot } from "valtio";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
selectMeBanishedZone,
selectOpBanishedZone,
} from "@/reducers/duel/banishedZoneSlice";
import { matStore } from "@/stores";
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
export const BanishedZone = () => {
// const meBanishedZone = useAppSelector(selectMeBanishedZone).inner;
// const opBanishedZone = useAppSelector(selectOpBanishedZone).inner;
const meBanishedZone = useSnapshot(matStore.banishedZones.me);
const opBanishedZone = useSnapshot(matStore.banishedZones.op);
......
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectMeDeck, selectOpDeck } from "@/reducers/duel/deckSlice";
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
const NeosConfig = useConfig();
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
export const CommonDeck = () => {
// const meDeck = useAppSelector(selectMeDeck).inner;
// const opDeck = useAppSelector(selectOpDeck).inner;
import { matStore } from "@/stores";
export const CommonDeck = () => {
const meDeck = useSnapshot(matStore.decks.me);
const opDeck = useSnapshot(matStore.decks.op);
......
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
selectMeExtraDeck,
selectOpExtraDeck,
} from "@/reducers/duel/extraDeckSlice";
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
const NeosConfig = useConfig();
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
import { matStore } from "@/stores";
export const ExtraDeck = () => {
// const meExtraDeck = useAppSelector(selectMeExtraDeck).inner;
// const opExtraDeck = useAppSelector(selectOpExtraDeck).inner;
const meExtraDeck = useSnapshot(matStore.extraDecks.me);
const opExtraDeck = useSnapshot(matStore.extraDecks.op);
......
import * as BABYLON from "@babylonjs/core";
import { useSnapshot } from "valtio";
import { ygopro } from "@/api";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectMeMagics, selectOpMagics } from "@/reducers/duel/magicSlice";
import { clearMagicPlaceInteractivities } from "@/reducers/duel/mod";
import { clearAllPlaceInteradtivities, matStore } from "@/stores";
import { cardSlotRotation } from "../utils";
import { FixedSlot } from "./FixedSlot";
import { Depth } from "./SingleSlot";
const NeosConfig = useConfig();
import { matStore, clearAllPlaceInteradtivities } from "@/valtioStores";
import { useSnapshot } from "valtio";
export const Field = () => {
// const meField = useAppSelector(selectMeMagics).inner.find(
// (_, sequence) => sequence == 5
// );
// const opField = useAppSelector(selectOpMagics).inner.find(
// (_, sequence) => sequence == 5
// );
// 这儿的find可能是出于某种考虑,以后再深思
const meFieldState = matStore.magics.me[5];
const meField = useSnapshot(meFieldState);
......@@ -29,7 +18,8 @@ export const Field = () => {
const opField = useSnapshot(opFieldState);
const clearPlaceInteractivitiesAction = (controller: number) =>
matStore.magics.of(controller).clearPlaceInteractivity();
clearAllPlaceInteradtivities(controller, ygopro.CardZone.MZONE); // 应该是对的
return (
<>
{meField ? (
......@@ -38,7 +28,6 @@ export const Field = () => {
sequence={0}
position={fieldPosition(0)}
rotation={cardSlotRotation(false)}
// clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
) : (
......@@ -50,7 +39,6 @@ export const Field = () => {
sequence={0}
position={fieldPosition(1)}
rotation={cardSlotRotation(true)}
// clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
) : (
......
import * as BABYLON from "@babylonjs/core";
import { ActionCreatorWithPayload } from "@reduxjs/toolkit";
import { useRef } from "react";
import { useSnapshot } from "valtio";
import { ygopro } from "@/api";
import { sendSelectPlaceResponse } from "@/api/ocgcore/ocgHelper";
import { sendSelectPlaceResponse, ygopro } from "@/api";
import { useConfig } from "@/config";
import { useClick } from "@/hook";
// import { CardState } from "@/reducers/duel/generic";
import {
setCardListModalInfo,
setCardListModalIsOpen,
setCardModalCounters,
setCardModalInteractivies,
setCardModalIsOpen,
setCardModalMeta,
} from "@/reducers/duel/mod";
import { store } from "@/store";
import { interactTypeToString } from "../utils";
import { useSnapshot, INTERNAL_Snapshot } from "valtio";
import {
clearAllIdleInteractivities,
type CardState,
clearAllPlaceInteradtivities,
messageStore,
} from "@/valtioStores";
} from "@/stores";
import { interactTypeToString } from "../utils";
const NeosConfig = useConfig();
......@@ -43,13 +29,11 @@ export const FixedSlot = (props: {
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
deffenseRotation?: BABYLON.Vector3;
// clearPlaceInteractivitiesAction: ActionCreatorWithPayload<number, string>;
clearPlaceInteractivitiesAction: (controller: number) => void;
}) => {
const planeRef = useRef(null);
const snapState = useSnapshot(props.state);
// const snapState = props.state;
const rotation =
snapState.location.position === ygopro.CardPosition.DEFENSE ||
snapState.location.position === ygopro.CardPosition.FACEUP_DEFENSE ||
......@@ -58,7 +42,6 @@ export const FixedSlot = (props: {
: props.rotation;
const edgesWidth = 2.0;
const edgesColor = BABYLON.Color4.FromColor3(BABYLON.Color3.Yellow());
// const dispatch = store.dispatch;
const faceDown =
snapState.location.position === ygopro.CardPosition.FACEDOWN_DEFENSE ||
......@@ -69,8 +52,6 @@ export const FixedSlot = (props: {
(_event) => {
if (snapState.placeInteractivity) {
sendSelectPlaceResponse(snapState.placeInteractivity.response);
// dispatch(props.clearPlaceInteractivitiesAction(0));
// dispatch(props.clearPlaceInteractivitiesAction(1));
// 其实不应该从外面传进来的...
// props.clearPlaceInteractivitiesAction(0);
// props.clearPlaceInteractivitiesAction(1);
......@@ -78,26 +59,13 @@ export const FixedSlot = (props: {
clearAllPlaceInteradtivities(1);
} else if (snapState.occupant) {
// 中央弹窗展示选中卡牌信息
// dispatch(setCardModalMeta(snapState.occupant));
messageStore.cardModal.meta = snapState.occupant;
// dispatch(
// setCardModalInteractivies(
// snapState.idleInteractivities.map((interactivity) => {
// return {
// desc: interactTypeToString(interactivity.interactType),
// response: interactivity.response,
// };
// })
// )
// );
messageStore.cardModal.interactivies =
snapState.idleInteractivities.map((interactivity) => ({
desc: interactTypeToString(interactivity.interactType),
response: interactivity.response,
}));
// dispatch(setCardModalCounters(snapState.counters));
messageStore.cardModal.counters = snapState.counters;
// dispatch(setCardModalIsOpen(true));
messageStore.cardModal.isOpen = true;
// 侧边栏展示超量素材信息
......@@ -105,22 +73,11 @@ export const FixedSlot = (props: {
snapState.overlay_materials &&
snapState.overlay_materials.length > 0
) {
// dispatch(
// setCardListModalInfo(
// snapState.overlay_materials?.map((overlay) => {
// return {
// meta: overlay,
// interactivies: [],
// };
// }) || []
// )
// );
messageStore.cardListModal.list =
snapState.overlay_materials?.map((overlay) => ({
meta: overlay,
interactivies: [],
})) || [];
// dispatch(setCardListModalIsOpen(true));
messageStore.cardListModal.isOpen = true;
}
}
......
import * as BABYLON from "@babylonjs/core";
import { useSnapshot } from "valtio";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
selectMeGraveyard,
selectOpGraveyard,
} from "@/reducers/duel/graveyardSlice";
import { matStore } from "@/stores";
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
export const Graveyard = () => {
// const meGraveyard = useAppSelector(selectMeGraveyard).inner;
// const opGraveyard = useAppSelector(selectOpGraveyard).inner;
const meGraveyard = useSnapshot(matStore.graveyards.me);
const opGraveyard = useSnapshot(matStore.graveyards.op);
......
import * as BABYLON from "@babylonjs/core";
import { useEffect, useRef, useState } from "react";
import { useHover } from "react-babylonjs";
import { INTERNAL_Snapshot, useSnapshot } from "valtio";
import { useConfig } from "@/config";
import { useAppSelector, useClick } from "@/hook";
// import { CardState } from "@/reducers/duel/generic";
import { selectMeHands, selectOpHands } from "@/reducers/duel/handsSlice";
import {
setCardModalInteractivies,
setCardModalIsOpen,
setCardModalMeta,
} from "@/reducers/duel/mod";
import { store } from "@/store";
import { matStore, type CardState, messageStore } from "@/valtioStores";
import { useSnapshot, INTERNAL_Snapshot } from "valtio";
import { useClick } from "@/hook";
import { type CardState, matStore, messageStore } from "@/stores";
import { animated, useSpring } from "../spring";
import { interactTypeToString, zip } from "../utils";
......@@ -32,12 +24,8 @@ export const Hands = () => {
const opHandsState = matStore.hands.op;
const meHandsSnap = useSnapshot(meHandsState);
const opHandsSnap = useSnapshot(opHandsState);
// const meHands = useAppSelector(selectMeHands).inner;
// const opHands = useAppSelector(selectOpHands).inner;
const meHandPositions = handPositons(0, meHandsSnap);
const opHandPositions = handPositons(1, opHandsSnap);
// const meHandPositions = handPositons(0, meHandsState);
// const opHandPositions = handPositons(1, opHandsState);
return (
<>
......@@ -49,7 +37,6 @@ export const Hands = () => {
sequence={idx}
position={position}
rotation={handRotation}
// cover={(id) => `${NeosConfig.cardImgUrl}/${id}.jpg`}
/>
);
})}
......@@ -61,7 +48,6 @@ export const Hands = () => {
sequence={idx}
position={position}
rotation={handRotation}
// cover={(_) => `${NeosConfig.assetsPath}/card_back.jpg`}
back={true}
/>
);
......@@ -89,7 +75,6 @@ const CHand = (props: {
const state = props.state;
const [hovered, setHovered] = useState(false);
const position = props.position;
// const dispatch = store.dispatch;
const [spring, api] = useSpring(
() => ({
......@@ -124,26 +109,14 @@ const CHand = (props: {
useClick(
() => {
if (state.occupant) {
// dispatch(setCardModalMeta(state.occupant));
messageStore.cardModal.meta = state.occupant;
}
// dispatch(
// setCardModalInteractivies(
// state.idleInteractivities.map((interactive) => {
// return {
// desc: interactTypeToString(interactive.interactType),
// response: interactive.response,
// };
// })
// )
// );
messageStore.cardModal.interactivies = state.idleInteractivities.map(
(interactive) => ({
desc: interactTypeToString(interactive.interactType),
response: interactive.response,
})
);
// dispatch(setCardModalIsOpen(true));
messageStore.cardModal.isOpen = true;
},
planeRef,
......@@ -162,7 +135,6 @@ const CHand = (props: {
rotation={props.rotation}
enableEdgesRendering
edgesWidth={
// state.idleInteractivities.length > 0 || state.placeInteractivities
state.idleInteractivities.length > 0 || state.placeInteractivity
? edgesWidth
: 0
......@@ -172,7 +144,6 @@ const CHand = (props: {
<animated.standardMaterial
name={`hand-mat-${props.sequence}`}
diffuseTexture={
// new BABYLON.Texture(props.cover(state.occupant?.id || 0))
new BABYLON.Texture(
props.back
? `${NeosConfig.assetsPath}/card_back.jpg`
......
import * as BABYLON from "@babylonjs/core";
import { type INTERNAL_Snapshot, useSnapshot } from "valtio";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
// import { CardState } from "@/reducers/duel/generic";
import { selectMeMagics, selectOpMagics } from "@/reducers/duel/magicSlice";
import { clearMagicPlaceInteractivities } from "@/reducers/duel/mod";
import { type CardState, matStore } from "@/stores";
import { cardSlotRotation, zip } from "../utils";
import { FixedSlot } from "./FixedSlot";
import { matStore, type CardState } from "@/valtioStores";
import { useSnapshot, type INTERNAL_Snapshot } from "valtio";
const NeosConfig = useConfig();
// TODO: use config
const left = -2.15;
......@@ -19,9 +14,6 @@ const gap = 1.05;
const transform = NeosConfig.ui.card.transform;
export const Magics = () => {
// const meMagics = useAppSelector(selectMeMagics).inner;
// const opMagics = useAppSelector(selectOpMagics).inner;
const meMagicState = matStore.magics.me;
const opMagicState = matStore.magics.op;
const meMagicsSnap = useSnapshot(meMagicState);
......@@ -46,7 +38,6 @@ export const Magics = () => {
sequence={sequence}
position={position}
rotation={cardSlotRotation(false)}
// clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
);
......@@ -61,7 +52,6 @@ export const Magics = () => {
sequence={sequence}
position={position}
rotation={cardSlotRotation(true)}
// clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
);
......
import "react-babylonjs";
import * as BABYLON from "@babylonjs/core";
import { type INTERNAL_Snapshot, useSnapshot } from "valtio";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
// import { CardState } from "@/reducers/duel/generic";
import { clearMonsterPlaceInteractivities } from "@/reducers/duel/mod";
import {
selectMeMonsters,
selectOpMonsters,
} from "@/reducers/duel/monstersSlice";
import { type CardState, matStore } from "@/stores";
import { cardSlotDefenceRotation, cardSlotRotation, zip } from "../utils";
import { FixedSlot } from "./FixedSlot";
import { matStore, type CardState, messageStore } from "@/valtioStores";
import { useSnapshot, type INTERNAL_Snapshot } from "valtio";
const NeosConfig = useConfig();
const transform = NeosConfig.ui.card.transform;
const floating = NeosConfig.ui.card.floating;
......@@ -29,9 +21,6 @@ const clearPlaceInteractivitiesAction = (controller: number) => {
};
export const Monsters = () => {
// const meMonsters = useAppSelector(selectMeMonsters).inner;
// const opMonsters = useAppSelector(selectOpMonsters).inner;
const meMonstersStore = matStore.monsters.me;
const opMonstersStore = matStore.monsters.op;
const meMonstersSnap = useSnapshot(meMonstersStore);
......@@ -51,7 +40,6 @@ export const Monsters = () => {
position={position}
rotation={cardSlotRotation(false)}
deffenseRotation={cardSlotDefenceRotation()}
// clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
))}
......@@ -65,7 +53,6 @@ export const Monsters = () => {
position={position}
rotation={cardSlotRotation(true)}
deffenseRotation={cardSlotDefenceRotation()}
// clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
))}
......@@ -101,7 +88,6 @@ const ExtraMonsters = (props: {
position={leftPosition}
rotation={meRotation}
deffenseRotation={cardSlotDefenceRotation()}
// clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
<FixedSlot
......@@ -110,7 +96,6 @@ const ExtraMonsters = (props: {
position={rightPosition}
rotation={meRotation}
deffenseRotation={cardSlotDefenceRotation()}
// clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
<FixedSlot
......@@ -119,7 +104,7 @@ const ExtraMonsters = (props: {
position={rightPosition}
rotation={opRotation}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
<FixedSlot
state={opRight}
......@@ -127,7 +112,7 @@ const ExtraMonsters = (props: {
position={leftPosition}
rotation={opRotation}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
</>
);
......
import * as BABYLON from "@babylonjs/core";
import { useRef } from "react";
import { useSnapshot } from "valtio";
import { useConfig } from "@/config";
import { useClick } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
import {
setCardListModalInfo,
setCardListModalIsOpen,
} from "@/reducers/duel/mod";
import { store } from "@/store";
import { type CardState, messageStore } from "@/stores";
import { interactTypeToString } from "../utils";
import { useSnapshot } from "valtio";
import { messageStore } from "@/valtioStores";
const NeosConfig = useConfig();
const transform = NeosConfig.ui.card.transform;
......@@ -26,7 +19,6 @@ export const SingleSlot = (props: {
}) => {
const snapState = useSnapshot(props.state);
const boxRef = useRef(null);
// const dispatch = store.dispatch;
const edgeRender =
snapState.find((item) =>
item === undefined ? false : item.idleInteractivities.length > 0
......@@ -37,25 +29,6 @@ export const SingleSlot = (props: {
useClick(
(_event) => {
if (snapState.length != 0) {
// dispatch(
// setCardListModalInfo(
// snapState
// .filter(
// (item) => item.occupant !== undefined && item.occupant.id !== 0
// )
// .map((item) => {
// return {
// meta: item.occupant,
// interactivies: item.idleInteractivities.map((interactivy) => {
// return {
// desc: interactTypeToString(interactivy.interactType),
// response: interactivy.response,
// };
// }),
// };
// })
// )
// );
messageStore.cardListModal.list = snapState
.filter(
(item) => item.occupant !== undefined && item.occupant.id !== 0
......
import { InteractType } from "@/reducers/duel/generic";
import { InteractType } from "@/stores";
export function interactTypeToString(t: InteractType): string {
switch (t) {
......
......@@ -4,22 +4,13 @@ import {
TableOutlined,
} from "@ant-design/icons";
import { Button, Modal } from "antd";
import React, { useContext, useEffect } from "react";
import React, { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useSnapshot } from "valtio";
import { sendHandResult, sendTpResult } from "@/api/ocgcore/ocgHelper";
import { sendHandResult, sendTpResult } from "@/api";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectDuelHsStart } from "@/reducers/duel/mod";
import {
selectHandSelectAble,
selectTpSelectAble,
unSelectHandAble,
unSelectTpAble,
} from "@/reducers/moraSlice";
import { store } from "@/store";
import { moraStore, matStore } from "@/valtioStores";
import { matStore, moraStore } from "@/stores";
const {
automation: { isAiMode, isAiFirst },
......@@ -30,14 +21,8 @@ const Mora = () => {
const snapMora = useSnapshot(moraStore);
const snapMatInitInfo = useSnapshot(matStore.initInfo);
// const dispatch = store.dispatch;
// const selectHandAble = useAppSelector(selectHandSelectAble);
// const selectTpAble = useAppSelector(selectTpSelectAble);
// const duelHsStart = useAppSelector(selectDuelHsStart);
const selectHandAble = snapMora.selectHandAble;
const selectTpAble = snapMora.selectTpAble;
// const duelHsStart = snapMora.duelStart;
const navigate = useNavigate();
const { player, passWd, ip } = useParams<{
......@@ -48,12 +33,10 @@ const Mora = () => {
const handleSelectMora = (selected: string) => {
sendHandResult(selected);
// dispatch(unSelectHandAble());
moraStore.selectHandAble = false;
};
const handleSelectTp = (isFirst: boolean) => {
sendTpResult(isFirst);
// dispatch(unSelectTpAble());
moraStore.selectTpAble = false;
};
......
......@@ -19,34 +19,18 @@ import {
Space,
Upload,
} from "antd";
import React, { useContext, useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import rustInit from "rust-src";
import { useSnapshot } from "valtio";
import YGOProDeck from "ygopro-deck-encode";
import { initStrings, sendHsReady, sendHsStart, sendUpdateDeck } from "@/api";
import { DeckManager, fetchDeck, type IDeck } from "@/api/deck";
import {
sendHsReady,
sendHsStart,
sendUpdateDeck,
} from "@/api/ocgcore/ocgHelper";
import { initStrings } from "@/api/strings";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import socketMiddleWare, { socketCmd } from "@/middleware/socket";
import sqliteMiddleWare, { sqliteCmd } from "@/middleware/sqlite";
import { selectChat } from "@/reducers/chatSlice";
import { initMeExtraDeckMeta } from "@/reducers/duel/extraDeckSlice";
import { selectJoined } from "@/reducers/joinSlice";
import { selectDuelStart } from "@/reducers/moraSlice";
import {
selectIsHost,
selectPlayer0,
selectPlayer1,
} from "@/reducers/playerSlice";
import { store } from "@/store";
import { valtioStore } from "@/valtioStores";
import { store } from "@/stores";
const NeosConfig = useConfig();
......@@ -58,7 +42,7 @@ const {
} = useConfig();
const WaitRoom = () => {
const state = valtioStore;
const state = store;
const snap = useSnapshot(state);
const params = useParams<{
player?: string;
......@@ -103,13 +87,6 @@ const WaitRoom = () => {
}
}, []);
const dispatch = store.dispatch;
// const joined = useAppSelector(selectJoined);
// const chat = useAppSelector(selectChat);
// const isHost = useAppSelector(selectIsHost);
// const player0 = useAppSelector(selectPlayer0);
// const player1 = useAppSelector(selectPlayer1);
// const duelStart = useAppSelector(selectDuelStart);
const [api, contextHolder] = notification.useNotification();
const joined = snap.joinStore.value;
......@@ -160,11 +137,7 @@ const WaitRoom = () => {
const onDeckReady = async (deck: IDeck) => {
sendUpdateDeck(deck);
await dispatch(
initMeExtraDeckMeta({ controler: 0, codes: deck.extra?.reverse() || [] })
);
// FIXME 直接写成了me,但不确定是不是对的
valtioStore.matStore.extraDecks.me.add(deck.extra?.reverse() || []);
store.matStore.extraDecks.me.add(deck.extra?.reverse() || []);
setChoseDeck(true);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment