Commit 469799ab authored by timel's avatar timel

Merge branch 'dev/valtio'

parents f96af3a5 bdd10692
......@@ -3,7 +3,8 @@
"extends": [
"plugin:prettier/recommended",
"plugin:import/recommended",
"plugin:import/typescript"
"plugin:import/typescript",
"plugin:valtio/recommended"
],
"plugins": [
"@typescript-eslint",
......
......@@ -36,6 +36,7 @@
"react-scripts": "^2.1.3",
"socket.io-client": "^4.6.1",
"sql.js": "^1.8.0",
"valtio": "^1.10.4",
"vite-plugin-svgr": "^2.4.0",
"web-vitals": "^2.1.4",
"ygopro-deck-encode": "^1.0.3"
......@@ -52,7 +53,6 @@
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-simple-import-sort": "^10.0.0",
"prettier": "^2.8.7",
"sass": "^1.61.0",
"typescript": "^4.9.5",
......@@ -8783,6 +8783,75 @@
"eslint": ">=5.0.0"
}
},
"node_modules/eslint-plugin-react-hooks": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz",
"integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==",
"engines": {
"node": ">=10"
},
"peerDependencies": {
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
}
},
"node_modules/eslint-plugin-react/node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
"integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
"dependencies": {
"esutils": "^2.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/eslint-plugin-react/node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"engines": {
"node": ">=4.0"
}
},
"node_modules/eslint-plugin-react/node_modules/resolve": {
"version": "2.0.0-next.4",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz",
"integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==",
"dependencies": {
"is-core-module": "^2.9.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/eslint-plugin-react/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/eslint-plugin-testing-library": {
"version": "5.10.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.10.2.tgz",
"integrity": "sha512-f1DmDWcz5SDM+IpCkEX0lbFqrrTs8HRsEElzDEqN/EBI0hpRj8Cns5+IVANXswE8/LeybIJqPAOQIFu2j5Y5sw==",
"dependencies": {
"@typescript-eslint/utils": "^5.43.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0",
"npm": ">=6"
},
"peerDependencies": {
"eslint": "^7.5.0 || ^8.0.0"
}
},
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
......@@ -19793,15 +19862,13 @@
"node": ">= 0.10"
}
},
"node_modules/prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw=="
},
"node_modules/pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
"node_modules/proxy-addr/node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/psl": {
"version": "1.9.0",
......@@ -26640,6 +26707,14 @@
"spdx-expression-parse": "^3.0.0"
}
},
"node_modules/v8-to-istanbul/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"engines": {
"node": ">= 8"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
......@@ -35259,6 +35334,14 @@
"dev": true,
"requires": {}
},
"eslint-plugin-testing-library": {
"version": "5.10.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.10.2.tgz",
"integrity": "sha512-f1DmDWcz5SDM+IpCkEX0lbFqrrTs8HRsEElzDEqN/EBI0hpRj8Cns5+IVANXswE8/LeybIJqPAOQIFu2j5Y5sw==",
"requires": {
"@typescript-eslint/utils": "^5.43.0"
}
},
"eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
......@@ -43819,18 +43902,15 @@
"requires": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"dependencies": {
"ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
}
}
},
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw=="
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
},
"psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
......@@ -49099,8 +49179,16 @@
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"requires": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
"@types/istanbul-lib-coverage": "^2.0.1",
"convert-source-map": "^1.6.0",
"source-map": "^0.7.3"
},
"dependencies": {
"source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="
}
}
},
"vary": {
......@@ -26,6 +26,8 @@ import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import { BrowserRouter } from "react-router-dom";
import { ValtioProvider } from "@/valtioStores";
import { store } from "./store";
import Neos from "./ui/Neos";
......@@ -36,12 +38,14 @@ root.render(
<React.StrictMode>
<BrowserRouter>
<Provider store={store}>
<ConfigProvider
theme={{ algorithm: theme.darkAlgorithm }}
locale={zhCN}
>
<Neos />
</ConfigProvider>
<ValtioProvider>
<ConfigProvider
theme={{ algorithm: theme.darkAlgorithm }}
locale={zhCN}
>
<Neos />
</ConfigProvider>
</ValtioProvider>
</Provider>
</BrowserRouter>
</React.StrictMode>
......
......@@ -15,13 +15,6 @@ import {
initBanishedZoneImpl,
removeBanishedZoneImpl,
} from "./banishedZoneSlice";
import {
addGraveyardIdleInteractivitiesImpl,
graveyardCase,
GraveyardState,
initGraveyardImpl,
removeGraveyardImpl,
} from "./cemeretySlice";
import {
clearAllIdleInteractivitiesImpl,
clearAllPlaceInteractivitiesImpl,
......@@ -35,6 +28,13 @@ import {
ExtraDeckState,
removeExtraDeckImpl,
} from "./extraDeckSlice";
import {
addGraveyardIdleInteractivitiesImpl,
graveyardCase,
GraveyardState,
initGraveyardImpl,
removeGraveyardImpl,
} from "./graveyardSlice";
import {
addHandsIdleInteractivityImpl,
clearHandsIdleInteractivityImpl,
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import MsgMove = ygopro.StocGameMessage.MsgMove;
import { fetchBanishedZoneMeta } from "@/reducers/duel/banishedZoneSlice";
import { fetchGraveyardMeta } from "@/reducers/duel/cemeretySlice";
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 {
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { player0DeckInfo, player1DeckInfo } from "@/reducers/playerSlice";
import { store } from "@/store";
import { playerStore } from "@/valtioStores";
// FIXME: player0 不一定是当前玩家
export default function handleDeckCount(pb: ygopro.YgoStocMsg) {
......@@ -15,6 +16,12 @@ export default function handleDeckCount(pb: ygopro.YgoStocMsg) {
})
);
playerStore.player0.deckInfo = {
mainCnt: deckCount.meMain,
extraCnt: deckCount.meExtra,
sideCnt: deckCount.meSide,
};
dispath(
player1DeckInfo({
mainCnt: deckCount.opMain,
......@@ -22,4 +29,10 @@ export default function handleDeckCount(pb: ygopro.YgoStocMsg) {
sideCnt: deckCount.opSide,
})
);
playerStore.player1.deckInfo = {
mainCnt: deckCount.opMain,
extraCnt: deckCount.opExtra,
sideCnt: deckCount.opSide,
};
}
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { selectHandAble } from "@/reducers/moraSlice";
import { store } from "@/store";
import { moraStore } from "@/valtioStores";
export default function handleSelectHand(_: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
dispatch(selectHandAble());
moraStore.selectHandAble = true;
}
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { selectTpAble } from "@/reducers/moraSlice";
import { store } from "@/store";
import { moraStore } from "@/valtioStores";
export default function handleSelectTp(_: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
dispatch(selectTpAble());
moraStore.selectTpAble = true;
}
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { postChat } from "@/reducers/chatSlice";
import { store } from "@/store";
import { chatStore } from "@/valtioStores";
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/ocgcore/idl/ocgcore";
import { duelStart } from "@/reducers/moraSlice";
import { store } from "@/store";
import { moraStore } from "@/valtioStores";
export default function handleDuelStart(_pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
dispatch(duelStart());
moraStore.duelStart = true;
}
......@@ -7,6 +7,7 @@ import {
player1Update,
} from "@/reducers/playerSlice";
import { store } from "@/store";
import { playerStore } from "@/valtioStores";
const READY_STATE = "ready";
const NO_READY_STATE = "not ready";
......@@ -48,25 +49,30 @@ export default function handleHsPlayerChange(pb: ygopro.YgoStocMsg) {
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;
}
default: {
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { player0Enter, player1Enter } from "@/reducers/playerSlice";
import { store } from "@/store";
import { playerStore } from "@/valtioStores";
export default function handleHsPlayerEnter(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
......@@ -12,5 +13,6 @@ export default function handleHsPlayerEnter(pb: ygopro.YgoStocMsg) {
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/ocgcore/idl/ocgcore";
import { observerChange } from "@/reducers/playerSlice";
import { store } from "@/store";
import { playerStore } from "@/valtioStores";
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/ocgcore/idl/ocgcore";
import { setJoined } from "@/reducers/joinSlice";
import { store } from "@/store";
import { joinStore } from "@/valtioStores";
export default function handleJoinGame(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
......@@ -8,4 +9,6 @@ export default function handleJoinGame(pb: ygopro.YgoStocMsg) {
const msg = pb.stoc_join_game;
// todo
dispatch(setJoined());
joinStore.value = true;
}
......@@ -6,6 +6,7 @@ import {
updateIsHost,
} from "@/reducers/playerSlice";
import { store } from "@/store";
import { playerStore } from "@/valtioStores";
const NO_READY_STATE = "not ready";
......@@ -22,12 +23,20 @@ export default function handleTypeChange(pb: ygopro.YgoStocMsg) {
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 { MessageOutlined } from "@ant-design/icons";
import { Timeline, TimelineItemProps } from "antd";
import React, { useEffect, useState } from "react";
import React, { useContext, useEffect, useState } from "react";
import { useSnapshot } from "valtio";
import { useAppSelector } from "@/hook";
import { selectChat } from "@/reducers/chatSlice";
import { valtioContext } from "@/valtioStores";
export const DuelTimeLine = () => {
const [items, setItems] = useState<TimelineItemProps[]>([]);
const chat = useAppSelector(selectChat);
const stateChat = useContext(valtioContext).chatStore;
const snapChat = useSnapshot(stateChat);
// const chat = snapChat.message;
useEffect(() => {
setItems((prev) =>
prev.concat([
......
......@@ -5,7 +5,7 @@ import { useAppSelector } from "@/hook";
import {
selectMeGraveyard,
selectOpGraveyard,
} from "@/reducers/duel/cemeretySlice";
} from "@/reducers/duel/graveyardSlice";
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
......
......@@ -4,8 +4,9 @@ import {
TableOutlined,
} from "@ant-design/icons";
import { Button, Modal } from "antd";
import React, { useEffect } from "react";
import React, { useContext, useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { useSnapshot } from "valtio";
import { sendHandResult, sendTpResult } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
......@@ -18,6 +19,7 @@ import {
unSelectTpAble,
} from "@/reducers/moraSlice";
import { store } from "@/store";
import { valtioContext } from "@/valtioStores";
const {
automation: { isAiMode, isAiFirst },
......@@ -25,10 +27,18 @@ const {
} = useConfig();
const Mora = () => {
const stateMora = useContext(valtioContext).moraStore;
const snapMora = useSnapshot(stateMora);
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<{
player?: string;
......@@ -39,10 +49,12 @@ const Mora = () => {
const handleSelectMora = (selected: string) => {
sendHandResult(selected);
dispatch(unSelectHandAble());
stateMora.selectHandAble = false;
};
const handleSelectTp = (isFirst: boolean) => {
sendTpResult(isFirst);
dispatch(unSelectTpAble());
stateMora.selectTpAble = false;
};
useEffect(() => {
......
......@@ -19,9 +19,10 @@ import {
Space,
Upload,
} from "antd";
import React, { useEffect, useState } from "react";
import React, { useContext, 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 { DeckManager, fetchDeck, type IDeck } from "@/api/deck";
......@@ -45,6 +46,7 @@ import {
selectPlayer1,
} from "@/reducers/playerSlice";
import { store } from "@/store";
import { valtioContext } from "@/valtioStores";
const NeosConfig = useConfig();
......@@ -56,6 +58,8 @@ const {
} = useConfig();
const WaitRoom = () => {
const state = useContext(valtioContext);
const snap = useSnapshot(state);
const params = useParams<{
player?: string;
passWd?: string;
......@@ -108,6 +112,13 @@ const WaitRoom = () => {
const duelStart = useAppSelector(selectDuelStart);
const [api, contextHolder] = notification.useNotification();
// const joined = snap.joinStore.value;
// const chat = snap.chatStore.message;
// const isHost = snap.playerStore.isHost;
// const player0 = snap.playerStore.player0;
// const player1 = snap.playerStore.player1;
// const duelStart = snap.moraStore.duelStart;
// FIXME: 这些数据应该从`store`中获取
// TODO: 云卡组
const decks = [...DeckManager.keys()].map((deckName) => ({
......
import { proxy } from "valtio";
export interface ChatState {
message: string;
}
export const chatStore = proxy<ChatState>({
message: "",
});
import { proxy } from "valtio";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import type { DuelState } from "./types";
export const playMat = proxy<DuelState>({
opMagics: {
inner: Array(5)
.fill(null)
.map((_, i) => ({
location: {
location: ygopro.CardZone.SZONE,
},
idleInteractivities: [],
counters: {},
})),
},
});
import { proxy } from "valtio";
import type { CardMeta } from "@/api/cards";
import type { ygopro } from "@/api/ocgcore/idl/ocgcore";
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?: ygopro.StocGameMessage.MsgWin.ActionType;
waiting?: boolean;
unimplemented?: number; // 未处理的`Message`
}
export interface InitInfo {
masterRule?: string;
life: number;
deckSize: number;
extraSize: number;
}
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 interface DuelFieldState {
inner: CardState[];
}
export interface Interactivity<T> {
interactType: InteractType;
// 如果`interactType`是`ACTIVATE`,这个字段是对应的效果编号
activateIndex?: number;
// 如果`interactType`是`ATTACK`,这个字段表示是否可以直接攻击
directAttackAble?: boolean;
// 用户点击后,需要回传给服务端的`response`
response: T;
}
export enum InteractType {
// 可普通召唤
SUMMON = 1,
// 可特殊召唤
SP_SUMMON = 2,
// 可改变表示形式
POS_CHANGE = 3,
// 可前场放置
MSET = 4,
// 可后场放置
SSET = 5,
// 可发动效果
ACTIVATE = 6,
// 可作为位置选择
PLACE_SELECTABLE = 7,
// 可攻击
ATTACK = 8,
}
export interface HandState extends DuelFieldState {}
export interface MonsterState extends DuelFieldState {}
export interface MagicState extends DuelFieldState {}
export interface GraveyardState extends DuelFieldState {}
export interface BanishedZoneState extends DuelFieldState {}
export interface DeckState extends DuelFieldState {}
export interface ExtraDeckState extends DuelFieldState {}
export interface TimeLimit {
leftTime: number;
}
export interface HintState {
code: number;
msg?: string;
esHint?: string;
esSelectHint?: string;
}
export interface PhaseState {
currentPhase: string; // TODO 当前的阶段 应该改成enum
enableBp: boolean; // 允许进入战斗阶段
enableM2: boolean; // 允许进入M2阶段
enableEp: boolean; // 允许回合结束
}
export * from "./chatStore";
export * from "./joinStore";
export * from "./moraStore";
export * from "./playerStore";
import { createContext, type ReactNode, useRef } from "react";
import { proxy } from "valtio";
import { devtools } from "valtio/utils";
import { chatStore } from "./chatStore";
import { joinStore } from "./joinStore";
import { moraStore } from "./moraStore";
import { playerStore } from "./playerStore";
export const valtioStore = proxy({
playerStore,
chatStore,
joinStore,
moraStore,
});
devtools(valtioStore, { name: "valtio store", enabled: true });
/**
* 在组件之中使用valtio store
*/
export const valtioContext = createContext<typeof valtioStore>({} as any);
/**
* 包裹根节点,使得所有子组件都可以使用valtio store
*/
export const ValtioProvider: React.FC<{ children: ReactNode }> = ({
children,
}) => {
const state = useRef(valtioStore).current;
return (
<valtioContext.Provider value={state}>{children}</valtioContext.Provider>
);
};
import { proxy } from "valtio";
export interface JoinState {
value: boolean;
}
export const joinStore = proxy<JoinState>({
value: false,
});
import { proxy } from "valtio";
export interface MoraState {
duelStart: boolean;
selectHandAble: boolean;
selectTpAble: boolean;
}
export const moraStore = proxy<MoraState>({
duelStart: false,
selectHandAble: false,
selectTpAble: false,
});
import { proxy } from "valtio";
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;
}
export const playerStore = proxy<PlayerState>({
player0: {},
player1: {},
observerCount: 0,
isHost: false,
});
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