Commit c77fde8c authored by timel's avatar timel

feat: valtio store logic 70%

parent 6f35a67e
......@@ -27,6 +27,7 @@
"antd": "^5.4.0",
"axios": "^0.27.2",
"google-protobuf": "^3.21.2",
"lodash-es": "^4.17.21",
"react": "^18.2.0",
"react-babylonjs": "^3.1.15",
"react-dom": "^18.2.0",
......@@ -45,6 +46,7 @@
"@babylonjs/core": "^5.54.0",
"@babylonjs/gui": "^5.54.0",
"@types/google-protobuf": "^3.15.6",
"@types/lodash-es": "^4.17.7",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@vitejs/plugin-react": "^3.1.0",
......@@ -3656,6 +3658,21 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
"node_modules/@types/lodash": {
"version": "4.14.194",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz",
"integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==",
"dev": true
},
"node_modules/@types/lodash-es": {
"version": "4.17.7",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.7.tgz",
"integrity": "sha512-z0ptr6UI10VlU6l5MYhGwS4mC8DZyYer2mCoyysZtSF7p26zOX8UpbrV0YpNYLGS8K4PUFIyEr62IMFFjveSiQ==",
"dev": true,
"dependencies": {
"@types/lodash": "*"
}
},
"node_modules/@types/node": {
"version": "16.18.23",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.23.tgz",
......@@ -15348,6 +15365,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
......@@ -31242,6 +31264,21 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
"dev": true
},
"@types/lodash": {
"version": "4.14.194",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz",
"integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==",
"dev": true
},
"@types/lodash-es": {
"version": "4.17.7",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.7.tgz",
"integrity": "sha512-z0ptr6UI10VlU6l5MYhGwS4mC8DZyYer2mCoyysZtSF7p26zOX8UpbrV0YpNYLGS8K4PUFIyEr62IMFFjveSiQ==",
"dev": true,
"requires": {
"@types/lodash": "*"
}
},
"@types/node": {
"version": "16.18.23",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.23.tgz",
......@@ -40451,6 +40488,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
import { ygopro } from "@/api";
import { ygopro, fetchCard, getCardStr } 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";
export default (selectOption: MsgSelectOption, dispatch: AppDispatch) => {
export default async (selectOption: MsgSelectOption, dispatch: AppDispatch) => {
const player = selectOption.player;
const options = selectOption.options;
......@@ -13,4 +14,15 @@ export default (selectOption: MsgSelectOption, dispatch: AppDispatch) => {
}
dispatch(setOptionModalIsOpen(true));
await Promise.all(
options.map(async ({ code, response }) => {
const meta = await fetchCard(code >> 4);
const msg = getCardStr(meta, code & 0xf) || "[?]";
const newResponse = { msg, response };
messageStore.optionModal.options.push(newResponse);
})
);
messageStore.optionModal.isOpen = true;
};
......@@ -4,7 +4,7 @@ import {
setPositionModalPositions,
} from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { matStore } from "@/valtioStores";
import { messageStore } from "@/valtioStores";
type MsgSelectPosition = ygopro.StocGameMessage.MsgSelectPosition;
......@@ -17,4 +17,9 @@ export default (selectPosition: MsgSelectPosition, dispatch: AppDispatch) => {
);
dispatch(setPositionModalIsOpen(true));
messageStore.positionModal.positions = positions.map(
(position) => position.position
);
messageStore.positionModal.isOpen = true;
};
......@@ -7,25 +7,43 @@ import {
} from "@/reducers/duel/mod";
import { fetchCheckCardMetasV3 } from "@/reducers/duel/modal/checkCardModalV3Slice";
import { AppDispatch } from "@/store";
import MsgSelectSum = ygopro.StocGameMessage.MsgSelectSum;
import {
messageStore,
fetchCheckCardMetasV3 as FIXME_fetchCheckCardMetasV3,
} from "@/valtioStores";
type MsgSelectSum = ygopro.StocGameMessage.MsgSelectSum;
export default (selectSum: MsgSelectSum, dispatch: AppDispatch) => {
dispatch(setCheckCardModalV3OverFlow(selectSum.overflow != 0));
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({
mustSelect: true,
options: selectSum.must_select_cards,
});
dispatch(
fetchCheckCardMetasV3({
mustSelect: false,
options: selectSum.selectable_cards,
})
);
FIXME_fetchCheckCardMetasV3({
mustSelect: false,
options: selectSum.selectable_cards,
});
dispatch(setCheckCardModalV3IsOpen(true));
messageStore.checkCardModalV3.isOpen = true;
};
......@@ -7,18 +7,28 @@ import {
} from "@/reducers/duel/mod";
import { fetchCheckCardMetasV3 } from "@/reducers/duel/modal/checkCardModalV3Slice";
import { AppDispatch } from "@/store";
import MsgSelectTribute = ygopro.StocGameMessage.MsgSelectTribute;
import {
messageStore,
fetchCheckCardMetasV3 as FIXME_fetchCheckCardMetasV3,
} from "@/valtioStores";
type MsgSelectTribute = ygopro.StocGameMessage.MsgSelectTribute;
export default (selectTribute: MsgSelectTribute, dispatch: AppDispatch) => {
// 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,
......@@ -33,5 +43,18 @@ export default (selectTribute: MsgSelectTribute, dispatch: AppDispatch) => {
}),
})
);
FIXME_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,
};
}),
});
dispatch(setCheckCardModalV3IsOpen(true));
messageStore.checkCardModalV3.isOpen = true;
};
......@@ -8,24 +8,36 @@ import {
} from "@/reducers/duel/mod";
import { fetchCheckCardMetasV2 } from "@/reducers/duel/modal/checkCardModalV2Slice";
import { AppDispatch } from "@/store";
import MsgSelectUnselectCard = ygopro.StocGameMessage.MsgSelectUnselectCard;
import {
messageStore,
fetchCheckCardMetasV2 as FIXME_fetchCheckCardMetasV2,
} from "@/valtioStores";
type MsgSelectUnselectCard = ygopro.StocGameMessage.MsgSelectUnselectCard;
export default (
selectUnselectCard: MsgSelectUnselectCard,
{
finishable,
cancelable,
min,
max,
selectable_cards: selectableCards,
selected_cards: selectedCards,
}: MsgSelectUnselectCard,
dispatch: AppDispatch
) => {
const finishable = selectUnselectCard.finishable;
const cancelable = selectUnselectCard.cancelable;
const min = selectUnselectCard.min;
const max = selectUnselectCard.max;
const selectableCards = selectUnselectCard.selectable_cards;
const selectedCards = selectUnselectCard.selected_cards;
dispatch(setCheckCardModalV2IsOpen(true));
dispatch(setCheckCardModalV2FinishAble(finishable));
dispatch(setCheckCardModalV2CancelAble(cancelable));
dispatch(setCheckCardModalV2MinMax({ min, max }));
messageStore.checkCardModalV2.isOpen = true;
messageStore.checkCardModalV2.finishAble = finishable;
messageStore.checkCardModalV2.cancelAble = cancelable;
messageStore.checkCardModalV2.selectMin = min;
messageStore.checkCardModalV2.selectMax = max;
dispatch(
fetchCheckCardMetasV2({
selected: false,
......@@ -39,6 +51,17 @@ export default (
})
);
FIXME_fetchCheckCardMetasV2({
selected: false,
options: selectableCards.map((card) => {
return {
code: card.code,
location: card.location,
response: card.response,
};
}),
});
dispatch(
fetchCheckCardMetasV2({
selected: true,
......@@ -52,5 +75,18 @@ export default (
})
);
FIXME_fetchCheckCardMetasV2({
selected: true,
options: selectedCards.map((card) => {
return {
code: card.code,
location: card.location,
response: card.response,
};
}),
});
dispatch(setCheckCardModalV2ResponseAble(true));
messageStore.checkCardModalV2.responseable = true;
};
......@@ -2,6 +2,9 @@ import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
export default (_set: ygopro.StocGameMessage.MsgSet, dispatch: AppDispatch) => {
dispatch(fetchEsHintMeta({ originMsg: 1601 }));
FIXME_fetchEsHintMeta({ originMsg: 1601 });
};
......@@ -2,9 +2,12 @@ import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
export default (
_: ygopro.StocGameMessage.MsgSpSummoned,
dispatch: AppDispatch
) => {
dispatch(fetchEsHintMeta({ originMsg: 1606 }));
FIXME_fetchEsHintMeta({ originMsg: 1606 });
};
......@@ -2,6 +2,7 @@ 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
......@@ -12,4 +13,8 @@ export default (
cardID: spSummoning.code,
})
);
FIXME_fetchEsHintMeta({
originMsg: "「[?]」特殊召唤宣言时",
cardID: spSummoning.code,
});
};
......@@ -2,9 +2,12 @@ import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
export default (
_: ygopro.StocGameMessage.MsgSummoned,
dispatch: AppDispatch
) => {
dispatch(fetchEsHintMeta({ originMsg: 1604 }));
FIXME_fetchEsHintMeta({ originMsg: 1604 });
};
......@@ -2,6 +2,8 @@ import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
export default (
summoning: ygopro.StocGameMessage.MsgSummoning,
dispatch: AppDispatch
......@@ -12,4 +14,8 @@ export default (
cardID: summoning.code,
})
);
FIXME_fetchEsHintMeta({
originMsg: "「[?]」通常召唤宣言时",
cardID: summoning.code,
});
};
......@@ -2,9 +2,12 @@ import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { fetchEsHintMeta as FIXME_fetchEsHintMeta } from "@/valtioStores";
export default (
_swap: ygopro.StocGameMessage.MsgSwap,
dispatch: AppDispatch
) => {
dispatch(fetchEsHintMeta({ originMsg: 1602 }));
FIXME_fetchEsHintMeta({ originMsg: 1602 });
};
......@@ -3,9 +3,14 @@ import { sendTimeConfirm } from "@/api/ocgcore/ocgHelper";
import { updateTimeLimit } from "@/reducers/duel/mod";
import { store } from "@/store";
import { matStore } from "@/valtioStores";
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();
}
......@@ -3,6 +3,8 @@ import { useConfig } from "@/config";
import { setUnimplemented } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { matStore } from "@/valtioStores";
const NeosConfig = useConfig();
export default (
......@@ -11,5 +13,6 @@ export default (
) => {
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 MsgUpdateCounter = ygopro.StocGameMessage.MsgUpdateCounter;
import { getCardByLocation } from "@/valtioStores";
type MsgUpdateCounter = ygopro.StocGameMessage.MsgUpdateCounter;
export default (updateCounter: MsgUpdateCounter, dispatch: AppDispatch) => {
dispatch(updateMonsterCounters(updateCounter.toObject()));
const { location, count, action_type: counterType } = updateCounter;
const target = getCardByLocation(location); // 不太确定这个后面能不能相应,我不好说
if (target) {
switch (counterType) {
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;
}
}
}
};
......@@ -2,14 +2,22 @@ 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 MsgUpdateHp = ygopro.StocGameMessage.MsgUpdateHp;
export default (msgUpdateHp: MsgUpdateHp, dispatch: AppDispatch) => {
if (msgUpdateHp.type_ == MsgUpdateHp.ActionType.DAMAGE) {
dispatch(fetchEsHintMeta({ originMsg: "玩家收到伤害时" })); // TODO: i18n
FIXME_fetchEsHintMeta({ originMsg: "玩家收到伤害时" });
matStore.initInfo.at(msgUpdateHp.player).life -= msgUpdateHp.value;
} else if (msgUpdateHp.type_ == MsgUpdateHp.ActionType.RECOVER) {
dispatch(fetchEsHintMeta({ originMsg: "玩家生命值回复时" })); // TODO: i18n
FIXME_fetchEsHintMeta({ originMsg: "玩家生命值回复时" });
matStore.initInfo.at(msgUpdateHp.player).life += msgUpdateHp.value;
}
dispatch(updateHp(msgUpdateHp));
dispatch(updateHp(msgUpdateHp)); // 可以删除了
};
......@@ -2,6 +2,11 @@ import { ygopro } from "@/api";
import { clearAllIdleInteractivities, setWaiting } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import {
matStore,
clearAllIdleInteractivities as FIXME_clearAllIdleInteractivities,
} from "@/valtioStores";
export default (
_wait: ygopro.StocGameMessage.MsgWait,
dispatch: AppDispatch
......@@ -9,4 +14,8 @@ export default (
dispatch(clearAllIdleInteractivities(0));
dispatch(clearAllIdleInteractivities(1));
dispatch(setWaiting(true));
FIXME_clearAllIdleInteractivities(0);
FIXME_clearAllIdleInteractivities(1);
matStore.waiting = true;
};
......@@ -2,6 +2,9 @@ import { ygopro } from "@/api";
import { setResult } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { matStore } from "@/valtioStores";
export default (win: ygopro.StocGameMessage.MsgWin, dispatch: AppDispatch) => {
dispatch(setResult(win.type_));
matStore.result = win.type_;
};
import { proxy } from "valtio";
import { cloneDeep } from "lodash-es";
import { fetchCard } from "@/api/cards";
import { ygopro } from "@/api";
......@@ -38,48 +38,36 @@ const isMe = (player: number): boolean => {
}
};
const genDuel = <T>(obj: T) => {
const res = proxy({
me: cloneDeep(obj),
op: cloneDeep(obj),
at: (controller: number) => res[getWhom(controller)],
});
return res;
};
/**
* 生成一个指定长度的卡片数组
*/
const genBlock = (
location: ygopro.CardZone,
n: number
): BothSide<DuelFieldState> => {
return {
me: Array(n)
.fill(null)
.map((_) => ({
location: {
location,
},
idleInteractivities: [],
counters: {},
})),
op: Array(n)
.fill(null)
.map((_) => ({
location: {
location,
},
idleInteractivities: [],
counters: {},
})),
};
};
const genBlock = (location: ygopro.CardZone, n: number): DuelFieldState =>
Array(n)
.fill(null)
.map((_) => ({
location: {
location,
},
idleInteractivities: [],
counters: {},
}));
const initInfo: MatState["initInfo"] = proxy({
me: {
...genDuel({
masterRule: "UNKNOWN",
life: -1, // 特地设置一个不可能的值
deckSize: 0,
extraSize: 0,
},
op: {
masterRule: "UNKNOWN",
life: -1, // 特地设置一个不可能的值
deckSize: 0,
extraSize: 0,
},
}),
set: (controller: number, obj: Partial<InitInfo>) => {
initInfo[getWhom(controller)] = {
...initInfo[getWhom(controller)],
......@@ -116,25 +104,18 @@ const wrap = <T extends DuelFieldState>(
const res: CardsBothSide<T> = proxy({
...entity,
at: (controller: number) => {
return res[getWhom(controller)];
},
remove: (controller: number, sequence: number) => {
res[getWhom(controller)].splice(sequence, 1);
res.at(controller).splice(sequence, 1);
},
insert: async (controller: number, sequence: number, id: number) => {
const card = await genCard(controller, id);
res[getWhom(controller)].splice(sequence, 0, card);
res.at(controller).splice(sequence, 0, card);
},
add: async (controller: number, ids: number[]) => {
const cards = await Promise.all(
ids.map(async (id) => genCard(controller, id))
);
res[getWhom(controller)].splice(
res[getWhom(controller)].length,
0,
...cards
);
res.at(controller).splice(res.at(controller).length, 0, ...cards);
},
setOccupant: async (
controller: number,
......@@ -143,33 +124,31 @@ const wrap = <T extends DuelFieldState>(
position?: ygopro.CardPosition
) => {
const meta = await fetchCard(id);
const target = res[getWhom(controller)][sequence];
const target = res.at(controller)[sequence];
target.occupant = meta;
if (position) {
target.location.position = position;
}
},
removeOccupant: (controller: number, sequence: number) => {
res[getWhom(controller)][sequence].occupant = undefined;
res.at(controller)[sequence].occupant = undefined;
},
addIdleInteractivity: (
controller: number,
sequence: number,
interactivity: CardState["idleInteractivities"][number]
) => {
res[getWhom(controller)][sequence].idleInteractivities.push(
interactivity
);
res.at(controller)[sequence].idleInteractivities.push(interactivity);
},
clearIdleInteractivities: (controller: number, sequence: number) => {
res[getWhom(controller)][sequence].idleInteractivities = [];
clearIdleInteractivities: (controller: number) => {
res.at(controller).forEach((card) => (card.idleInteractivities = []));
},
setPlaceInteractivityType: (
controller: number,
sequence: number,
interactType: InteractType
) => {
res[getWhom(controller)][sequence].placeInteractivity = {
res.at(controller)[sequence].placeInteractivity = {
interactType: interactType,
response: {
controler: controller,
......@@ -178,8 +157,10 @@ const wrap = <T extends DuelFieldState>(
},
};
},
clearPlaceInteractivity: (controller: number, sequence: number) => {
res[getWhom(controller)][sequence].placeInteractivity = undefined;
clearPlaceInteractivity: (controller: number) => {
res
.at(controller)
.forEach((card) => (card.placeInteractivity = undefined));
},
});
return res;
......@@ -209,23 +190,27 @@ const getZone = (zone: ygopro.CardZone) => {
return matStore.extraDecks;
}
};
const { SZONE, MZONE, GRAVE, REMOVED, HAND, DECK, EXTRA } = ygopro.CardZone;
/**
* 💡 决斗盘状态仓库,本文件核心,
* 具体介绍可以点进`PlayMatState`去看
*/
export const matStore: MatState = proxy<MatState>({
magics: wrap(genBlock(ygopro.CardZone.SZONE, 6), ygopro.CardZone.SZONE),
monsters: wrap(genBlock(ygopro.CardZone.MZONE, 7), ygopro.CardZone.MZONE),
graveyards: wrap({ me: [], op: [] }, ygopro.CardZone.GRAVE),
banishedZones: wrap({ me: [], op: [] }, ygopro.CardZone.REMOVED),
hands: wrap({ me: [], op: [] }, ygopro.CardZone.HAND),
decks: wrap({ me: [], op: [] }, ygopro.CardZone.DECK),
extraDecks: wrap({ me: [], op: [] }, ygopro.CardZone.EXTRA),
magics: wrap(genDuel(genBlock(SZONE, 6)), SZONE),
monsters: wrap(genDuel(genBlock(MZONE, 7)), MZONE),
graveyards: wrap(genDuel([]), GRAVE),
banishedZones: wrap(genDuel([]), REMOVED),
hands: wrap(genDuel([]), HAND),
decks: wrap(genDuel([]), DECK),
extraDecks: wrap(genDuel([]), EXTRA),
timeLimits: {
// 时间限制
me: 0,
op: 0,
...genDuel(-1),
set: (controller: number, time: number) => {
matStore.timeLimits[getWhom(controller)] = time;
},
},
initInfo,
......
......@@ -6,6 +6,7 @@ import type { ygopro } from "@/api";
export interface BothSide<T> {
me: T;
op: T;
at: (controller: number) => T;
}
export interface CardsBothSide<T extends DuelFieldState> extends BothSide<T> {
......@@ -33,7 +34,7 @@ export interface CardsBothSide<T extends DuelFieldState> extends BothSide<T> {
interactivity: CardState["idleInteractivities"][number]
) => void;
/** 移除 idle 的交互性 */
clearIdleInteractivities: (controller: number, sequence: number) => void;
clearIdleInteractivities: (controller: number) => void;
/** 设置 place 的交互种类 */
setPlaceInteractivityType: (
controller: number,
......@@ -41,7 +42,7 @@ export interface CardsBothSide<T extends DuelFieldState> extends BothSide<T> {
interactType: InteractType
) => void;
/** 移除 place 的交互性 */
clearPlaceInteractivity: (controller: number, sequence: number) => void;
clearPlaceInteractivity: (controller: number) => void;
}
export interface MatState {
......@@ -65,7 +66,9 @@ export interface MatState {
extraDecks: CardsBothSide<ExtraDeckState>; // 双方的额外卡组状态
timeLimits: BothSide<number>; // 双方的时间限制
timeLimits: BothSide<number> & {
set: (controller: number, time: number) => void;
}; // 双方的时间限制
hint: HintState;
......
import { proxy } from "valtio";
import type { CardMeta } from "@/api/cards";
import { ygopro } from "@/api";
export const messageStore = proxy<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: [],
},
});
// >>> modal types >>>
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;
}[];
};
}
// <<< modal types <<<
export * from "./store";
export * from "./methods";
import { matStore } from "@/valtioStores";
export const clearAllIdleInteractivities = (controller: number) => {
matStore.banishedZones.clearIdleInteractivities(controller);
matStore.decks.clearIdleInteractivities(controller);
matStore.extraDecks.clearIdleInteractivities(controller);
matStore.graveyards.clearIdleInteractivities(controller);
matStore.hands.clearIdleInteractivities(controller);
matStore.magics.clearIdleInteractivities(controller);
matStore.monsters.clearIdleInteractivities(controller);
};
import { type ygopro, fetchCard } from "@/api";
import { getCardByLocation, messageStore } from "@/valtioStores";
export const fetchCheckCardMetasV2 = async ({
selected,
options,
}: {
selected: boolean;
options: {
code: number;
location: ygopro.CardLocation;
response: number;
name?: string;
desc?: string;
}[];
}) => {
const metas = await Promise.all(
options.map(async (option) => {
return await fetchCard(option.code, true);
})
);
for (const option of options) {
if (option.code == 0) {
const newCode = getCardByLocation(option.location)?.occupant?.id || 0;
option.code = newCode;
}
}
options.forEach((option) => {
metas.forEach((meta) => {
if (option.code == meta.id) {
option.name = meta.text.name;
option.desc = meta.text.desc;
}
});
});
if (selected) {
messageStore.checkCardModalV2.selectedOptions = options;
} else {
messageStore.checkCardModalV2.selectableOptions = options;
}
};
import { type ygopro, fetchCard } from "@/api";
import { getCardByLocation, messageStore } from "@/valtioStores";
export const fetchCheckCardMetasV3 = async ({
mustSelect,
options,
}: {
mustSelect: boolean;
options: {
code: number;
location: ygopro.CardLocation;
level1: number;
level2: number;
response: number;
}[];
}) => {
const metas = await Promise.all(
options.map(async (option) => {
return await fetchCard(option.code, true);
})
);
const newOptions = options.map((option) => {
if (option.code == 0) {
const newCode = getCardByLocation(option.location)?.occupant?.id || 0;
option.code = newCode;
}
return {
meta: { id: option.code, data: {}, text: {} },
level1: option.level1,
level2: option.level2,
response: option.response,
};
});
newOptions.forEach((option) => {
metas.forEach((meta) => {
if (option.meta.id == meta.id) {
option.meta = meta;
}
});
});
if (mustSelect) {
messageStore.checkCardModalV3.mustSelectList = newOptions;
} else {
messageStore.checkCardModalV3.selectAbleList = newOptions;
}
};
export * from "./fetchCheckCardMetasV2";
export * from "./fetchCheckCardMetasV3";
export * from "./clearAllIdleInteractivities";
import { proxy } from "valtio";
import type { ModalState } from "./types";
export const messageStore = proxy<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: [],
},
});
// >>> modal types >>>
import type { CardMeta, ygopro } from "@/api";
type CardLocation = ygopro.CardLocation;
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;
}[];
};
}
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