Commit 63390a0c authored by Chunchi Che's avatar Chunchi Che

refactor store

parent b6f722a4
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { cardStore, fetchEsHintMeta } from "@/stores";
import { callCardAttack } from "@/ui/Duel/PlayMat/Card"; import { callCardAttack } from "@/ui/Duel/PlayMat/Card";
export default async (attack: ygopro.StocGameMessage.MsgAttack) => { import { fetchEsHintMeta } from "./util";
export default async (
container: Container,
attack: ygopro.StocGameMessage.MsgAttack,
) => {
const context = container.context;
fetchEsHintMeta({ fetchEsHintMeta({
context,
originMsg: "「[?]」攻击时", originMsg: "「[?]」攻击时",
location: attack.attacker_location, location: attack.attacker_location,
}); });
const attacker = cardStore.at( const attacker = context.cardStore.at(
attack.attacker_location.zone, attack.attacker_location.zone,
attack.attacker_location.controller, attack.attacker_location.controller,
attack.attacker_location.sequence, attack.attacker_location.sequence,
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/stores"; import { Container } from "@/container";
export default (_: ygopro.StocGameMessage.MsgAttackDisabled) => { import { fetchEsHintMeta } from "./util";
fetchEsHintMeta({ originMsg: "攻击被无效时" });
export default (
container: Container,
_: ygopro.StocGameMessage.MsgAttackDisabled,
) => {
fetchEsHintMeta({ context: container.context, originMsg: "攻击被无效时" });
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { cardStore } from "@/stores"; import { Container } from "@/container";
export default (becomeTarget: ygopro.StocGameMessage.MsgBecomeTarget) => { export default (
container: Container,
becomeTarget: ygopro.StocGameMessage.MsgBecomeTarget,
) => {
const context = container.context;
for (const location of becomeTarget.locations) { for (const location of becomeTarget.locations) {
const target = cardStore.at( const target = context.cardStore.at(
location.zone, location.zone,
location.controller, location.controller,
location.sequence, location.sequence,
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { cardStore, matStore, placeStore } from "@/stores"; import { Container } from "@/container";
export default (_chainEnd: ygopro.StocGameMessage.MsgChainEnd) => { export default (
container: Container,
_chainEnd: ygopro.StocGameMessage.MsgChainEnd,
) => {
console.info(`<ChainEnd>chain has been end`); console.info(`<ChainEnd>chain has been end`);
const context = container.context;
while (true) { while (true) {
const chain = matStore.chains.pop(); const chain = context.matStore.chains.pop();
if (chain === undefined) { if (chain === undefined) {
break; break;
} }
const block = placeStore.of(chain); const block = context.placeStore.of(context, chain);
if (block) { if (block) {
block.chainIndex.pop(); block.chainIndex.pop();
} else { } else {
...@@ -22,7 +26,7 @@ export default (_chainEnd: ygopro.StocGameMessage.MsgChainEnd) => { ...@@ -22,7 +26,7 @@ export default (_chainEnd: ygopro.StocGameMessage.MsgChainEnd) => {
// 因此在连锁结束的时候把selected标记清掉。 // 因此在连锁结束的时候把selected标记清掉。
// //
// TODO: 这里每次都要全部遍历一遍,后续可以优化下 // TODO: 这里每次都要全部遍历一遍,后续可以优化下
for (const card of cardStore.inner) { for (const card of context.cardStore.inner) {
card.targeted = false; card.targeted = false;
} }
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { matStore, placeStore } from "@/stores"; import { Container } from "@/container";
// FIXME: 处理连锁会存在三种结果: // FIXME: 处理连锁会存在三种结果:
// 1. Solved - 已处理; // 1. Solved - 已处理;
...@@ -9,15 +9,19 @@ import { matStore, placeStore } from "@/stores"; ...@@ -9,15 +9,19 @@ import { matStore, placeStore } from "@/stores";
// 第一种MSG后端会在每一个连锁点处理完(不管是无效还是禁用)发给前端, // 第一种MSG后端会在每一个连锁点处理完(不管是无效还是禁用)发给前端,
// 第二第三种只会在特定情况下发,用于UI展示。 // 第二第三种只会在特定情况下发,用于UI展示。
// 这里暂时只处理第一种。 // 这里暂时只处理第一种。
export default async (chainSolved: ygopro.StocGameMessage.MsgChainSolved) => { export default async (
container: Container,
chainSolved: ygopro.StocGameMessage.MsgChainSolved,
) => {
console.info(`<ChainSolved>solved_index = ${chainSolved.solved_index}`); console.info(`<ChainSolved>solved_index = ${chainSolved.solved_index}`);
const context = container.context;
const location = matStore.chains const location = context.matStore.chains
.splice(chainSolved.solved_index - 1, 1) .splice(chainSolved.solved_index - 1, 1)
.at(0); .at(0);
if (location) { if (location) {
// 设置被连锁状态为空,解除连锁 // 设置被连锁状态为空,解除连锁
const block = placeStore.of(location); const block = context.placeStore.of(context, location);
if (block) { if (block) {
block.chainIndex.pop(); block.chainIndex.pop();
} else { } else {
...@@ -26,7 +30,7 @@ export default async (chainSolved: ygopro.StocGameMessage.MsgChainSolved) => { ...@@ -26,7 +30,7 @@ export default async (chainSolved: ygopro.StocGameMessage.MsgChainSolved) => {
} else { } else {
console.warn( console.warn(
`pop from chains return null! solved_index=${chainSolved.solved_index}, `pop from chains return null! solved_index=${chainSolved.solved_index},
len of chains in store=${matStore.chains.length}`, len of chains in store=${context.matStore.chains.length}`,
); );
} }
}; };
import { fetchCard, ygopro } from "@/api"; import { fetchCard, ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { cardStore, fetchEsHintMeta, matStore, placeStore } from "@/stores";
import { callCardFocus } from "@/ui/Duel/PlayMat/Card"; import { callCardFocus } from "@/ui/Duel/PlayMat/Card";
export default async (chaining: ygopro.StocGameMessage.MsgChaining) => { import { fetchEsHintMeta } from "./util";
export default async (
container: Container,
chaining: ygopro.StocGameMessage.MsgChaining,
) => {
playEffect(AudioActionType.SOUND_ACTIVATE); playEffect(AudioActionType.SOUND_ACTIVATE);
const context = container.context;
fetchEsHintMeta({ fetchEsHintMeta({
context,
originMsg: "「[?]」被发动时", originMsg: "「[?]」被发动时",
cardID: chaining.code, cardID: chaining.code,
}); });
...@@ -13,14 +20,14 @@ export default async (chaining: ygopro.StocGameMessage.MsgChaining) => { ...@@ -13,14 +20,14 @@ export default async (chaining: ygopro.StocGameMessage.MsgChaining) => {
const location = chaining.location; const location = chaining.location;
// 将`location`添加到连锁栈 // 将`location`添加到连锁栈
matStore.chains.push(location); context.matStore.chains.push(location);
const target = cardStore.find(location); const target = context.cardStore.find(location);
if (target) { if (target) {
// 设置连锁序号 // 设置连锁序号
const block = placeStore.of(location); const block = context.placeStore.of(context, location);
if (block) { if (block) {
block.chainIndex.push(matStore.chains.length); block.chainIndex.push(context.matStore.chains.length);
} else { } else {
console.warn(`<Chaining>block from ${location} is null`); console.warn(`<Chaining>block from ${location} is null`);
} }
...@@ -40,7 +47,9 @@ export default async (chaining: ygopro.StocGameMessage.MsgChaining) => { ...@@ -40,7 +47,9 @@ export default async (chaining: ygopro.StocGameMessage.MsgChaining) => {
// 发动效果动画 // 发动效果动画
await callCardFocus(target.uuid); await callCardFocus(target.uuid);
console.color("blue")(`${target.meta.text.name} chaining`); console.color("blue")(`${target.meta.text.name} chaining`);
console.info(`<Chaining>chain stack length = ${matStore.chains.length}`); console.info(
`<Chaining>chain stack length = ${context.matStore.chains.length}`,
);
} else { } else {
console.warn(`<Chaining>target from ${location} is null`); console.warn(`<Chaining>target from ${location} is null`);
} }
......
import { fetchCard, ygopro } from "@/api"; import { fetchCard, ygopro } from "@/api";
import { Container } from "@/container";
import { sleep } from "@/infra"; import { sleep } from "@/infra";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { cardStore } from "@/stores";
import { callCardFocus, callCardMove } from "@/ui/Duel/PlayMat/Card"; import { callCardFocus, callCardMove } from "@/ui/Duel/PlayMat/Card";
const { MZONE, SZONE } = ygopro.CardZone; const { MZONE, SZONE } = ygopro.CardZone;
...@@ -10,13 +10,21 @@ const { FACEUP_ATTACK, FACEDOWN_ATTACK, FACEDOWN_DEFENSE, FACEDOWN } = ...@@ -10,13 +10,21 @@ const { FACEUP_ATTACK, FACEDOWN_ATTACK, FACEDOWN_DEFENSE, FACEDOWN } =
const WAIT_TIME = 100; const WAIT_TIME = 100;
export default async (confirmCards: ygopro.StocGameMessage.MsgConfirmCards) => { export default async (
container: Container,
confirmCards: ygopro.StocGameMessage.MsgConfirmCards,
) => {
playEffect(AudioActionType.SOUND_REVEAL); playEffect(AudioActionType.SOUND_REVEAL);
const context = container.context;
const cards = confirmCards.cards; const cards = confirmCards.cards;
console.color("pink")(`confirmCards: ${cards}`); console.color("pink")(`confirmCards: ${cards}`);
for (const card of cards) { for (const card of cards) {
const target = cardStore.at(card.location, card.controller, card.sequence); const target = context.cardStore.at(
card.location,
card.controller,
card.sequence,
);
if (target) { if (target) {
// 设置`occupant` // 设置`occupant`
......
import { fetchCard, ygopro } from "@/api"; import { fetchCard, ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { cardStore, fetchEsHintMeta } from "@/stores";
import { callCardMove } from "@/ui/Duel/PlayMat/Card"; import { callCardMove } from "@/ui/Duel/PlayMat/Card";
export default async (draw: ygopro.StocGameMessage.MsgDraw) => { import { fetchEsHintMeta } from "./util";
fetchEsHintMeta({ originMsg: "玩家抽卡时" });
export default async (
container: Container,
draw: ygopro.StocGameMessage.MsgDraw,
) => {
const context = container.context;
fetchEsHintMeta({ context, originMsg: "玩家抽卡时" });
const drawLength = draw.cards.length; const drawLength = draw.cards.length;
// 将卡从卡组移到手牌:设置zone、occupant、sequence // 将卡从卡组移到手牌:设置zone、occupant、sequence
const handsLength = cardStore.at(ygopro.CardZone.HAND, draw.player).length; const handsLength = context.cardStore.at(
const newHands = cardStore ygopro.CardZone.HAND,
draw.player,
).length;
const newHands = context.cardStore
.at(ygopro.CardZone.DECK, draw.player) .at(ygopro.CardZone.DECK, draw.player)
.sort((a, b) => a.location.sequence - b.location.sequence) .sort((a, b) => a.location.sequence - b.location.sequence)
.slice(-drawLength); .slice(-drawLength);
...@@ -29,7 +38,7 @@ export default async (draw: ygopro.StocGameMessage.MsgDraw) => { ...@@ -29,7 +38,7 @@ export default async (draw: ygopro.StocGameMessage.MsgDraw) => {
// 抽卡动画 // 抽卡动画
await Promise.all( await Promise.all(
cardStore context.cardStore
.at(ygopro.CardZone.HAND, draw.player) .at(ygopro.CardZone.HAND, draw.player)
.map((card) => callCardMove(card.uuid)), .map((card) => callCardMove(card.uuid)),
); );
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { placeStore } from "@/stores";
import MsgFieldDisabled = ygopro.StocGameMessage.MsgFieldDisabled; import MsgFieldDisabled = ygopro.StocGameMessage.MsgFieldDisabled;
import { Container } from "@/container";
export default (fieldDisabled: MsgFieldDisabled) => { export default (container: Container, fieldDisabled: MsgFieldDisabled) => {
const context = container.context;
for (const action of fieldDisabled.actions) { for (const action of fieldDisabled.actions) {
switch (action.zone) { switch (action.zone) {
case ygopro.CardZone.MZONE: case ygopro.CardZone.MZONE:
case ygopro.CardZone.SZONE: case ygopro.CardZone.SZONE:
const block = placeStore.of(action); const block = context.placeStore.of(context, action);
if (block) { if (block) {
block.disabled = action.disabled; block.disabled = action.disabled;
} else { } else {
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/stores"; import { Container } from "@/container";
export default (_: ygopro.StocGameMessage.MsgFlipSummoned) => { import { fetchEsHintMeta } from "./util";
fetchEsHintMeta({ originMsg: 1608 });
export default (
container: Container,
_: ygopro.StocGameMessage.MsgFlipSummoned,
) => {
fetchEsHintMeta({ context: container.context, originMsg: 1608 });
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/stores"; import { Container } from "@/container";
export default (flipSummoning: ygopro.StocGameMessage.MsgFlipSummoning) => { import { fetchEsHintMeta } from "./util";
export default (
container: Container,
flipSummoning: ygopro.StocGameMessage.MsgFlipSummoning,
) => {
// playEffect(AudioActionType.SOUND_FILP); // playEffect(AudioActionType.SOUND_FILP);
fetchEsHintMeta({ fetchEsHintMeta({
context: container.context,
originMsg: "「[?]」反转召唤宣言时", originMsg: "「[?]」反转召唤宣言时",
cardID: flipSummoning.code, cardID: flipSummoning.code,
}); });
......
...@@ -111,7 +111,7 @@ export default async function handleGameMsg( ...@@ -111,7 +111,7 @@ export default async function handleGameMsg(
switch (msg.gameMsg) { switch (msg.gameMsg) {
case "start": { case "start": {
await onMsgStart(msg.start); await onMsgStart(container, msg.start);
// We should init agent when the MSG_START reached. // We should init agent when the MSG_START reached.
if (agent) await agent.init(); if (agent) await agent.init();
...@@ -119,27 +119,27 @@ export default async function handleGameMsg( ...@@ -119,27 +119,27 @@ export default async function handleGameMsg(
break; break;
} }
case "draw": { case "draw": {
await onMsgDraw(msg.draw); await onMsgDraw(container, msg.draw);
break; break;
} }
case "new_turn": { case "new_turn": {
onMsgNewTurn(msg.new_turn); onMsgNewTurn(container, msg.new_turn);
break; break;
} }
case "new_phase": { case "new_phase": {
onMsgNewPhase(msg.new_phase); onMsgNewPhase(container, msg.new_phase);
break; break;
} }
case "hint": { case "hint": {
await onMsgHint(msg.hint); await onMsgHint(container, msg.hint);
break; break;
} }
case "select_idle_cmd": { case "select_idle_cmd": {
onMsgSelectIdleCmd(msg.select_idle_cmd); onMsgSelectIdleCmd(container, msg.select_idle_cmd);
break; break;
} }
...@@ -149,7 +149,7 @@ export default async function handleGameMsg( ...@@ -149,7 +149,7 @@ export default async function handleGameMsg(
break; break;
} }
case "move": { case "move": {
await onMsgMove(msg.move); await onMsgMove(container, msg.move);
break; break;
} }
case "select_card": { case "select_card": {
...@@ -178,22 +178,22 @@ export default async function handleGameMsg( ...@@ -178,22 +178,22 @@ export default async function handleGameMsg(
break; break;
} }
case "shuffle_hand_extra": { case "shuffle_hand_extra": {
await onMsgShuffleHandExtra(msg.shuffle_hand_extra); await onMsgShuffleHandExtra(container, msg.shuffle_hand_extra);
break; break;
} }
case "select_battle_cmd": { case "select_battle_cmd": {
onMsgSelectBattleCmd(msg.select_battle_cmd); onMsgSelectBattleCmd(container, msg.select_battle_cmd);
break; break;
} }
case "pos_change": { case "pos_change": {
await onMsgPosChange(msg.pos_change); await onMsgPosChange(container, msg.pos_change);
break; break;
} }
case "select_unselect_card": { case "select_unselect_card": {
await onMsgSelectUnselectCard(msg.select_unselect_card); await onMsgSelectUnselectCard(container, msg.select_unselect_card);
break; break;
} }
...@@ -203,47 +203,47 @@ export default async function handleGameMsg( ...@@ -203,47 +203,47 @@ export default async function handleGameMsg(
break; break;
} }
case "update_hp": { case "update_hp": {
onMsgUpdateHp(msg.update_hp); onMsgUpdateHp(container, msg.update_hp);
break; break;
} }
case "win": { case "win": {
await onMsgWin(msg.win); await onMsgWin(container, msg.win);
break; break;
} }
case "wait": { case "wait": {
onMsgWait(msg.wait); onMsgWait(container, msg.wait);
break; break;
} }
case "update_data": { case "update_data": {
await onMsgUpdateData(msg.update_data); await onMsgUpdateData(container, msg.update_data);
break; break;
} }
case "reload_field": { case "reload_field": {
onMsgReloadField(msg.reload_field); onMsgReloadField(container, msg.reload_field);
break; break;
} }
case "select_sum": { case "select_sum": {
onMsgSelectSum(msg.select_sum); onMsgSelectSum(container, msg.select_sum);
break; break;
} }
case "select_tribute": { case "select_tribute": {
onMsgSelectTribute(msg.select_tribute); onMsgSelectTribute(container, msg.select_tribute);
break; break;
} }
case "update_counter": { case "update_counter": {
onMsgUpdateCounter(msg.update_counter); onMsgUpdateCounter(container, msg.update_counter);
break; break;
} }
case "select_counter": { case "select_counter": {
await onMsgSelectCounter(msg.select_counter); await onMsgSelectCounter(container, msg.select_counter);
break; break;
} }
...@@ -253,66 +253,66 @@ export default async function handleGameMsg( ...@@ -253,66 +253,66 @@ export default async function handleGameMsg(
break; break;
} }
case "set": { case "set": {
onMsgSet(msg.set); onMsgSet(container, msg.set);
break; break;
} }
case "swap": { case "swap": {
onMsgSwap(msg.swap); onMsgSwap(container, msg.swap);
break; break;
} }
case "attack": { case "attack": {
await onMsgAttack(msg.attack); await onMsgAttack(container, msg.attack);
break; break;
} }
case "attack_disable": { case "attack_disable": {
onMsgAttackDisable(msg.attack_disable); onMsgAttackDisable(container, msg.attack_disable);
break; break;
} }
case "chaining": { case "chaining": {
await onMsgChaining(msg.chaining); await onMsgChaining(container, msg.chaining);
break; break;
} }
case "chain_solved": { case "chain_solved": {
await onMsgChainSolved(msg.chain_solved); await onMsgChainSolved(container, msg.chain_solved);
break; break;
} }
case "chain_end": { case "chain_end": {
onMsgChainEnd(msg.chain_end); onMsgChainEnd(container, msg.chain_end);
break; break;
} }
case "summoning": { case "summoning": {
onMsgSummoning(msg.summoning); onMsgSummoning(container, msg.summoning);
break; break;
} }
case "summoned": { case "summoned": {
onMsgSummoned(msg.summoned); onMsgSummoned(container, msg.summoned);
break; break;
} }
case "flip_summoning": { case "flip_summoning": {
onMsgFlipSummoning(msg.flip_summoning); onMsgFlipSummoning(container, msg.flip_summoning);
break; break;
} }
case "flip_summoned": { case "flip_summoned": {
onMsgFilpSummoned(msg.flip_summoned); onMsgFilpSummoned(container, msg.flip_summoned);
break; break;
} }
case "sp_summoning": { case "sp_summoning": {
onMsgSpSummoning(msg.sp_summoning); onMsgSpSummoning(container, msg.sp_summoning);
break; break;
} }
case "sp_summoned": { case "sp_summoned": {
onMsgSpSummoned(msg.sp_summoned); onMsgSpSummoned(container, msg.sp_summoned);
break; break;
} }
...@@ -322,36 +322,36 @@ export default async function handleGameMsg( ...@@ -322,36 +322,36 @@ export default async function handleGameMsg(
break; break;
} }
case "lp_update": { case "lp_update": {
onLpUpdate(msg.lp_update); onLpUpdate(container, msg.lp_update);
break; break;
} }
case "confirm_cards": { case "confirm_cards": {
await onConfirmCards(msg.confirm_cards); await onConfirmCards(container, msg.confirm_cards);
break; break;
} }
case "become_target": { case "become_target": {
onMsgBecomeTarget(msg.become_target); onMsgBecomeTarget(container, msg.become_target);
break; break;
} }
case "toss": { case "toss": {
onMsgToss(msg.toss); onMsgToss(container, msg.toss);
break; break;
} }
case "shuffle_set_card": { case "shuffle_set_card": {
await onMsgShuffleSetCard(msg.shuffle_set_card); await onMsgShuffleSetCard(container, msg.shuffle_set_card);
break; break;
} }
case "field_disabled": { case "field_disabled": {
onMsgFieldDisabled(msg.field_disabled); onMsgFieldDisabled(container, msg.field_disabled);
break; break;
} }
case "shuffle_deck": { case "shuffle_deck": {
onMsgShuffleDeck(msg.shuffle_deck); onMsgShuffleDeck(container, msg.shuffle_deck);
break; break;
} }
...@@ -361,22 +361,22 @@ export default async function handleGameMsg( ...@@ -361,22 +361,22 @@ export default async function handleGameMsg(
break; break;
} }
case "hand_res": { case "hand_res": {
onMsgHandResult(msg.hand_res); onMsgHandResult(container, msg.hand_res);
break; break;
} }
case "swap_grave_deck": { case "swap_grave_deck": {
await onMsgSwapGraveDeck(msg.swap_grave_deck); await onMsgSwapGraveDeck(container, msg.swap_grave_deck);
break; break;
} }
case "sibyl_name": { case "sibyl_name": {
onMsgSibylName(msg.sibyl_name); onMsgSibylName(container, msg.sibyl_name);
break; break;
} }
case "unimplemented": { case "unimplemented": {
onUnimplemented(msg.unimplemented); onUnimplemented(container, msg.unimplemented);
break; break;
} }
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { matStore } from "@/stores";
import MsgHandResult = ygopro.StocGameMessage.MsgHandResult; import MsgHandResult = ygopro.StocGameMessage.MsgHandResult;
import { Container } from "@/container";
export default (res: MsgHandResult) => { export default (container: Container, res: MsgHandResult) => {
const context = container.context;
const { result1, result2 } = res; const { result1, result2 } = res;
matStore.handResults.set(0, result1); context.matStore.handResults.set(0, result1);
matStore.handResults.set(1, result2); context.matStore.handResults.set(1, result2);
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { import { fetchCommonHintMeta, fetchSelectHintMeta } from "@/stores";
fetchCommonHintMeta,
fetchEsHintMeta,
fetchSelectHintMeta,
} from "@/stores";
import MsgHint = ygopro.StocGameMessage.MsgHint; import MsgHint = ygopro.StocGameMessage.MsgHint;
import { Container } from "@/container";
export default async (hint: MsgHint) => { import { fetchEsHintMeta } from "./util";
export default async (container: Container, hint: MsgHint) => {
switch (hint.hint_type) { switch (hint.hint_type) {
case MsgHint.HintType.HINT_EVENT: { case MsgHint.HintType.HINT_EVENT: {
await fetchEsHintMeta({ originMsg: hint.hint_data }); await fetchEsHintMeta({
context: container.context,
originMsg: hint.hint_data,
});
break; break;
} }
case MsgHint.HintType.HINT_MESSAGE: { case MsgHint.HintType.HINT_MESSAGE: {
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { matStore } from "@/stores"; import { Container } from "@/container";
export default (lpUpdate: ygopro.StocGameMessage.MsgLpUpdate) => { export default (
container: Container,
lpUpdate: ygopro.StocGameMessage.MsgLpUpdate,
) => {
const player = lpUpdate.player; const player = lpUpdate.player;
const newLp = lpUpdate.new_lp; const newLp = lpUpdate.new_lp;
matStore.initInfo.of(player).life = newLp; container.context.matStore.initInfo.of(player).life = newLp;
}; };
import { fetchCard, ygopro } from "@/api"; import { fetchCard, ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { cardStore, CardType } from "@/stores"; import { CardType } from "@/stores";
import { callCardMove } from "@/ui/Duel/PlayMat/Card"; import { callCardMove } from "@/ui/Duel/PlayMat/Card";
import { REASON_DESTROY, REASON_MATERIAL, TYPE_TOKEN } from "../../common"; import { REASON_DESTROY, REASON_MATERIAL, TYPE_TOKEN } from "../../common";
...@@ -31,7 +32,8 @@ const overlayStack: ygopro.CardLocation[] = []; ...@@ -31,7 +32,8 @@ const overlayStack: ygopro.CardLocation[] = [];
* - 通过`meta.data.type`判断一张卡是否是衍生物。 * - 通过`meta.data.type`判断一张卡是否是衍生物。
* *
* */ * */
export default async (move: MsgMove) => { export default async (container: Container, move: MsgMove) => {
const context = container.context;
const code = move.code; const code = move.code;
const from = move.from; const from = move.from;
const to = move.to; const to = move.to;
...@@ -63,7 +65,7 @@ export default async (move: MsgMove) => { ...@@ -63,7 +65,7 @@ export default async (move: MsgMove) => {
if (from.is_overlay) { if (from.is_overlay) {
// 超量素材的去除 // 超量素材的去除
const overlayMaterial = cardStore.at( const overlayMaterial = context.cardStore.at(
from.zone, from.zone,
from.controller, from.controller,
from.sequence, from.sequence,
...@@ -79,14 +81,18 @@ export default async (move: MsgMove) => { ...@@ -79,14 +81,18 @@ export default async (move: MsgMove) => {
return; return;
} }
} else { } else {
const card = cardStore.at(from.zone, from.controller, from.sequence); const card = context.cardStore.at(
from.zone,
from.controller,
from.sequence,
);
if (card) { if (card) {
target = card; target = card;
} else { } else {
console.warn( console.warn(
`<Move>card from zone=${from.zone}, controller=${from.controller} sequence=${from.sequence} is null`, `<Move>card from zone=${from.zone}, controller=${from.controller} sequence=${from.sequence} is null`,
); );
console.info(cardStore.at(from.zone, from.controller)); console.info(context.cardStore.at(from.zone, from.controller));
return; return;
} }
} }
...@@ -105,7 +111,7 @@ export default async (move: MsgMove) => { ...@@ -105,7 +111,7 @@ export default async (move: MsgMove) => {
// 超量素材出栈 // 超量素材出栈
const xyzLocations = overlayStack.splice(0, overlayStack.length); const xyzLocations = overlayStack.splice(0, overlayStack.length);
for (const location of xyzLocations) { for (const location of xyzLocations) {
const overlayMaterial = cardStore.at( const overlayMaterial = context.cardStore.at(
location.zone, location.zone,
location.controller, location.controller,
location.sequence, location.sequence,
...@@ -132,8 +138,8 @@ export default async (move: MsgMove) => { ...@@ -132,8 +138,8 @@ export default async (move: MsgMove) => {
} }
// 维护sequence // 维护sequence
const fromCards = cardStore.at(from.zone, from.controller); const fromCards = context.cardStore.at(from.zone, from.controller);
const toCards = cardStore.at(to.zone, to.controller); const toCards = context.cardStore.at(to.zone, to.controller);
if ( if (
[HAND, GRAVE, REMOVED, DECK, EXTRA, TZONE].includes(from.zone) && [HAND, GRAVE, REMOVED, DECK, EXTRA, TZONE].includes(from.zone) &&
...@@ -149,7 +155,7 @@ export default async (move: MsgMove) => { ...@@ -149,7 +155,7 @@ export default async (move: MsgMove) => {
if (from.is_overlay) { if (from.is_overlay) {
// 超量素材的序号也需要维护 // 超量素材的序号也需要维护
const overlay_sequence = from.overlay_sequence; const overlay_sequence = from.overlay_sequence;
for (const overlay of cardStore.findOverlay( for (const overlay of context.cardStore.findOverlay(
from.zone, from.zone,
from.controller, from.controller,
from.sequence, from.sequence,
...@@ -182,7 +188,7 @@ export default async (move: MsgMove) => { ...@@ -182,7 +188,7 @@ export default async (move: MsgMove) => {
const p = callCardMove(target.uuid, { fromZone: from.zone }); const p = callCardMove(target.uuid, { fromZone: from.zone });
// 如果from或者to是手卡,那么需要刷新除了这张卡之外,这个玩家的所有手卡 // 如果from或者to是手卡,那么需要刷新除了这张卡之外,这个玩家的所有手卡
if ([from.zone, to.zone].includes(HAND)) { if ([from.zone, to.zone].includes(HAND)) {
const pHands = cardStore const pHands = context.cardStore
.at(HAND, target.location.controller) .at(HAND, target.location.controller)
.filter((c) => c.uuid !== target.uuid) .filter((c) => c.uuid !== target.uuid)
.map(async (c) => await callCardMove(c.uuid)); .map(async (c) => await callCardMove(c.uuid));
...@@ -193,7 +199,7 @@ export default async (move: MsgMove) => { ...@@ -193,7 +199,7 @@ export default async (move: MsgMove) => {
// 超量素材位置跟随超量怪兽移动 // 超量素材位置跟随超量怪兽移动
if (from.zone === MZONE && !from.is_overlay) { if (from.zone === MZONE && !from.is_overlay) {
for (const overlay of cardStore.findOverlay( for (const overlay of context.cardStore.findOverlay(
from.zone, from.zone,
from.controller, from.controller,
from.sequence, from.sequence,
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { matStore } from "@/stores";
export default (newPhase: ygopro.StocGameMessage.MsgNewPhase) => { export default (
container: Container,
newPhase: ygopro.StocGameMessage.MsgNewPhase,
) => {
playEffect(AudioActionType.SOUND_PHASE); playEffect(AudioActionType.SOUND_PHASE);
// ts本身还没有这么智能,所以需要手动指定类型 // ts本身还没有这么智能,所以需要手动指定类型
matStore.phase.currentPhase = newPhase.phase_type; container.context.matStore.phase.currentPhase = newPhase.phase_type;
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { matStore } from "@/stores";
export default (newTurn: ygopro.StocGameMessage.MsgNewTurn) => { export default (
container: Container,
newTurn: ygopro.StocGameMessage.MsgNewTurn,
) => {
playEffect(AudioActionType.SOUND_NEXT_TURN); playEffect(AudioActionType.SOUND_NEXT_TURN);
const context = container.context;
const player = newTurn.player; const player = newTurn.player;
matStore.currentPlayer = player; context.matStore.currentPlayer = player;
matStore.turnCount = matStore.turnCount + 1; context.matStore.turnCount = context.matStore.turnCount + 1;
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import MsgPosChange = ygopro.StocGameMessage.MsgPosChange; import MsgPosChange = ygopro.StocGameMessage.MsgPosChange;
import { cardStore, fetchEsHintMeta } from "@/stores"; import { Container } from "@/container";
import { callCardMove } from "@/ui/Duel/PlayMat/Card"; import { callCardMove } from "@/ui/Duel/PlayMat/Card";
export default async (posChange: MsgPosChange) => { import { fetchEsHintMeta } from "./util";
export default async (container: Container, posChange: MsgPosChange) => {
const context = container.context;
const { location, controller, sequence } = posChange.card_info; const { location, controller, sequence } = posChange.card_info;
const target = cardStore.at(location, controller, sequence); const target = context.cardStore.at(location, controller, sequence);
if (target) { if (target) {
target.location.position = posChange.cur_position; target.location.position = posChange.cur_position;
...@@ -17,6 +20,7 @@ export default async (posChange: MsgPosChange) => { ...@@ -17,6 +20,7 @@ export default async (posChange: MsgPosChange) => {
} }
fetchEsHintMeta({ fetchEsHintMeta({
context,
originMsg: 1600, originMsg: 1600,
}); });
}; };
import { v4 as v4uuid } from "uuid"; import { v4 as v4uuid } from "uuid";
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { cardStore, matStore } from "@/stores"; import { Container } from "@/container";
import { genCard } from "../utils"; import { genCard } from "../utils";
type MsgReloadField = ygopro.StocGameMessage.MsgReloadField; type MsgReloadField = ygopro.StocGameMessage.MsgReloadField;
export default (field: MsgReloadField) => { export default (container: Container, field: MsgReloadField) => {
const context = container.context;
// 重置 // 重置
cardStore.reset(); context.cardStore.reset();
const actions = field.actions; const actions = field.actions;
actions.forEach((action) => { actions.forEach((action) => {
const controller = action.player; const controller = action.player;
// 更新生命值 // 更新生命值
matStore.initInfo.of(controller).life = action.lp; context.matStore.initInfo.of(controller).life = action.lp;
// 更新卡片集合 // 更新卡片集合
const cards = action.zone_actions const cards = action.zone_actions
.map((zoneAction) => .map((zoneAction) =>
...@@ -44,6 +45,6 @@ export default (field: MsgReloadField) => { ...@@ -44,6 +45,6 @@ export default (field: MsgReloadField) => {
), ),
) )
.flat(); .flat();
cardStore.inner.push(...cards); context.cardStore.inner.push(...cards);
}); });
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { import { type Interactivity, InteractType } from "@/stores";
cardStore,
type Interactivity,
InteractType,
matStore,
} from "@/stores";
import MsgSelectBattleCmd = ygopro.StocGameMessage.MsgSelectBattleCmd; import MsgSelectBattleCmd = ygopro.StocGameMessage.MsgSelectBattleCmd;
import { Container } from "@/container";
export default async (selectBattleCmd: MsgSelectBattleCmd) => { export default async (
container: Container,
selectBattleCmd: MsgSelectBattleCmd,
) => {
const context = container.context;
const player = selectBattleCmd.player; const player = selectBattleCmd.player;
const cmds = selectBattleCmd.battle_cmds; const cmds = selectBattleCmd.battle_cmds;
// 先清掉之前的互动性 // 先清掉之前的互动性
// TODO: 确认这里在AI托管的模式下是否需要 // TODO: 确认这里在AI托管的模式下是否需要
cardStore.inner.forEach((card) => { context.cardStore.inner.forEach((card) => {
card.idleInteractivities = []; card.idleInteractivities = [];
}); });
...@@ -33,7 +33,7 @@ export default async (selectBattleCmd: MsgSelectBattleCmd) => { ...@@ -33,7 +33,7 @@ export default async (selectBattleCmd: MsgSelectBattleCmd) => {
[InteractType.ATTACK]: { directAttackAble: data.direct_attackable }, [InteractType.ATTACK]: { directAttackAble: data.direct_attackable },
}; };
const tmp = map[interactType]; // 添加额外信息 const tmp = map[interactType]; // 添加额外信息
const target = cardStore.at(location, player, sequence); const target = context.cardStore.at(location, player, sequence);
if (target) { if (target) {
target.idleInteractivities.push({ target.idleInteractivities.push({
...tmp, ...tmp,
...@@ -50,8 +50,8 @@ export default async (selectBattleCmd: MsgSelectBattleCmd) => { ...@@ -50,8 +50,8 @@ export default async (selectBattleCmd: MsgSelectBattleCmd) => {
} }
}); });
}); });
matStore.phase.enableM2 = selectBattleCmd.enable_m2; context.matStore.phase.enableM2 = selectBattleCmd.enable_m2;
matStore.phase.enableEp = selectBattleCmd.enable_ep; context.matStore.phase.enableEp = selectBattleCmd.enable_ep;
}; };
function battleTypeToInteracType( function battleTypeToInteracType(
......
...@@ -9,6 +9,7 @@ import { fetchCheckCardMeta } from "../utils"; ...@@ -9,6 +9,7 @@ import { fetchCheckCardMeta } from "../utils";
export default async (container: Container, selectCard: MsgSelectCard) => { export default async (container: Container, selectCard: MsgSelectCard) => {
const { cancelable, min, max, cards } = selectCard; const { cancelable, min, max, cards } = selectCard;
const conn = container.conn; const conn = container.conn;
const context = container.context;
// TODO: handle release_param // TODO: handle release_param
...@@ -19,6 +20,7 @@ export default async (container: Container, selectCard: MsgSelectCard) => { ...@@ -19,6 +20,7 @@ export default async (container: Container, selectCard: MsgSelectCard) => {
} }
const { selecteds, mustSelects, selectables } = await fetchCheckCardMeta( const { selecteds, mustSelects, selectables } = await fetchCheckCardMeta(
context,
cards, cards,
); );
await displaySelectActionsModal({ await displaySelectActionsModal({
......
import { sendSelectSingleResponse, ygopro } from "@/api"; import { sendSelectSingleResponse, ygopro } from "@/api";
import { Container } from "@/container"; import { Container } from "@/container";
import { ChainSetting, fetchSelectHintMeta, matStore } from "@/stores"; import { ChainSetting, fetchSelectHintMeta } from "@/stores";
import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal"; import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal";
import { fetchCheckCardMeta } from "../utils"; import { fetchCheckCardMeta } from "../utils";
...@@ -8,12 +8,13 @@ import { fetchCheckCardMeta } from "../utils"; ...@@ -8,12 +8,13 @@ import { fetchCheckCardMeta } from "../utils";
type MsgSelectChain = ygopro.StocGameMessage.MsgSelectChain; type MsgSelectChain = ygopro.StocGameMessage.MsgSelectChain;
export default async (container: Container, selectChain: MsgSelectChain) => { export default async (container: Container, selectChain: MsgSelectChain) => {
const conn = container.conn; const conn = container.conn;
const context = container.context;
const spCount = selectChain.special_count; const spCount = selectChain.special_count;
const forced = selectChain.forced; const forced = selectChain.forced;
const _hint0 = selectChain.hint0; const _hint0 = selectChain.hint0;
const _hint1 = selectChain.hint1; const _hint1 = selectChain.hint1;
const chains = selectChain.chains; const chains = selectChain.chains;
const chainSetting = matStore.chainSetting; const chainSetting = context.matStore.chainSetting;
if (chainSetting === ChainSetting.CHAIN_IGNORE) { if (chainSetting === ChainSetting.CHAIN_IGNORE) {
// 如果玩家配置了忽略连锁,直接回应后端并返回 // 如果玩家配置了忽略连锁,直接回应后端并返回
...@@ -73,6 +74,7 @@ export default async (container: Container, selectChain: MsgSelectChain) => { ...@@ -73,6 +74,7 @@ export default async (container: Container, selectChain: MsgSelectChain) => {
selectHintData: 203, selectHintData: 203,
}); });
const { selecteds, mustSelects, selectables } = await fetchCheckCardMeta( const { selecteds, mustSelects, selectables } = await fetchCheckCardMeta(
context,
chains, chains,
); );
await displaySelectActionsModal({ await displaySelectActionsModal({
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { cardStore } from "@/stores"; import { Container } from "@/container";
import { displayCheckCounterModal } from "@/ui/Duel/Message"; import { displayCheckCounterModal } from "@/ui/Duel/Message";
type MsgSelectCounter = ygopro.StocGameMessage.MsgSelectCounter; type MsgSelectCounter = ygopro.StocGameMessage.MsgSelectCounter;
export default async (selectCounter: MsgSelectCounter) => { export default async (
container: Container,
selectCounter: MsgSelectCounter,
) => {
const context = container.context;
await displayCheckCounterModal({ await displayCheckCounterModal({
counterType: selectCounter.counter_type, counterType: selectCounter.counter_type,
min: selectCounter.min, min: selectCounter.min,
options: selectCounter.options!.map(({ location, code, counter_count }) => { options: selectCounter.options!.map(({ location, code, counter_count }) => {
const id = cardStore.find(location)?.code; const id = context.cardStore.find(location)?.code;
const newCode = code ? code : id || 0; const newCode = code ? code : id || 0;
return { return {
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { import { type Interactivity, InteractType } from "@/stores";
cardStore,
type Interactivity,
InteractType,
matStore,
} from "@/stores";
import MsgSelectIdleCmd = ygopro.StocGameMessage.MsgSelectIdleCmd; import MsgSelectIdleCmd = ygopro.StocGameMessage.MsgSelectIdleCmd;
import { Container } from "@/container";
export default async (selectIdleCmd: MsgSelectIdleCmd) => { export default async (
container: Container,
selectIdleCmd: MsgSelectIdleCmd,
) => {
const context = container.context;
const player = selectIdleCmd.player; const player = selectIdleCmd.player;
const cmds = selectIdleCmd.idle_cmds; const cmds = selectIdleCmd.idle_cmds;
// 先清掉之前的互动性 // 先清掉之前的互动性
// TODO: 确认这里是否需要在AI托管的时候调用 // TODO: 确认这里是否需要在AI托管的时候调用
cardStore.inner.forEach((card) => { context.cardStore.inner.forEach((card) => {
card.idleInteractivities = []; card.idleInteractivities = [];
}); });
...@@ -32,7 +32,7 @@ export default async (selectIdleCmd: MsgSelectIdleCmd) => { ...@@ -32,7 +32,7 @@ export default async (selectIdleCmd: MsgSelectIdleCmd) => {
[InteractType.ACTIVATE]: { activateIndex: data.effect_description }, [InteractType.ACTIVATE]: { activateIndex: data.effect_description },
}; };
const tmp = map[interactType]; const tmp = map[interactType];
const target = cardStore.at(location, player, sequence); const target = context.cardStore.at(location, player, sequence);
if (target) { if (target) {
target.idleInteractivities.push({ target.idleInteractivities.push({
...tmp, ...tmp,
...@@ -50,8 +50,8 @@ export default async (selectIdleCmd: MsgSelectIdleCmd) => { ...@@ -50,8 +50,8 @@ export default async (selectIdleCmd: MsgSelectIdleCmd) => {
}); });
}); });
matStore.phase.enableBp = selectIdleCmd.enable_bp; context.matStore.phase.enableBp = selectIdleCmd.enable_bp;
matStore.phase.enableEp = selectIdleCmd.enable_ep; context.matStore.phase.enableEp = selectIdleCmd.enable_ep;
}; };
function idleTypeToInteractType( function idleTypeToInteractType(
......
import { sendSelectPlaceResponse, ygopro } from "@/api"; import { sendSelectPlaceResponse, ygopro } from "@/api";
import { Container } from "@/container"; import { Container } from "@/container";
import { InteractType, placeStore } from "@/stores"; import { InteractType } from "@/stores";
type MsgSelectPlace = ygopro.StocGameMessage.MsgSelectPlace; type MsgSelectPlace = ygopro.StocGameMessage.MsgSelectPlace;
export default async (container: Container, selectPlace: MsgSelectPlace) => { export default async (container: Container, selectPlace: MsgSelectPlace) => {
const conn = container.conn; const conn = container.conn;
const context = container.context;
if (selectPlace.count !== 1) { if (selectPlace.count !== 1) {
console.warn(`Unhandled case: ${selectPlace}`); console.warn(`Unhandled case: ${selectPlace}`);
return; return;
...@@ -26,7 +27,7 @@ export default async (container: Container, selectPlace: MsgSelectPlace) => { ...@@ -26,7 +27,7 @@ export default async (container: Container, selectPlace: MsgSelectPlace) => {
switch (place.zone) { switch (place.zone) {
case ygopro.CardZone.MZONE: case ygopro.CardZone.MZONE:
case ygopro.CardZone.SZONE: case ygopro.CardZone.SZONE:
const block = placeStore.of(place); const block = context.placeStore.of(context, place);
if (block) { if (block) {
block.interactivity = { block.interactivity = {
interactType: InteractType.PLACE_SELECTABLE, interactType: InteractType.PLACE_SELECTABLE,
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal"; import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal";
import { fetchCheckCardMeta } from "../utils"; import { fetchCheckCardMeta } from "../utils";
type MsgSelectSum = ygopro.StocGameMessage.MsgSelectSum; type MsgSelectSum = ygopro.StocGameMessage.MsgSelectSum;
export default async (selectSum: MsgSelectSum) => { export default async (container: Container, selectSum: MsgSelectSum) => {
const context = container.context;
const { const {
selecteds: selecteds1, selecteds: selecteds1,
mustSelects: mustSelect1, mustSelects: mustSelect1,
selectables: selectable1, selectables: selectable1,
} = await fetchCheckCardMeta(selectSum.must_select_cards, false, true); } = await fetchCheckCardMeta(
context,
selectSum.must_select_cards,
false,
true,
);
const { const {
selecteds: selecteds2, selecteds: selecteds2,
mustSelects: mustSelect2, mustSelects: mustSelect2,
selectables: selectable2, selectables: selectable2,
} = await fetchCheckCardMeta(selectSum.selectable_cards); } = await fetchCheckCardMeta(context, selectSum.selectable_cards);
await displaySelectActionsModal({ await displaySelectActionsModal({
overflow: selectSum.overflow !== 0, overflow: selectSum.overflow !== 0,
totalLevels: selectSum.level_sum, totalLevels: selectSum.level_sum,
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal"; import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal";
import { fetchCheckCardMeta } from "../utils"; import { fetchCheckCardMeta } from "../utils";
type MsgSelectTribute = ygopro.StocGameMessage.MsgSelectTribute; type MsgSelectTribute = ygopro.StocGameMessage.MsgSelectTribute;
export default async (selectTribute: MsgSelectTribute) => { export default async (
container: Container,
selectTribute: MsgSelectTribute,
) => {
const { selecteds, mustSelects, selectables } = await fetchCheckCardMeta( const { selecteds, mustSelects, selectables } = await fetchCheckCardMeta(
container.context,
selectTribute.selectable_cards, selectTribute.selectable_cards,
); );
// TODO: 当玩家选择卡数大于`max`时,是否也合法? // TODO: 当玩家选择卡数大于`max`时,是否也合法?
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { cardStore, matStore } from "@/stores"; import { Container } from "@/container";
import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal"; import { displaySelectActionsModal } from "@/ui/Duel/Message/SelectActionsModal";
import { fetchCheckCardMeta } from "../utils"; import { fetchCheckCardMeta } from "../utils";
import { isAllOnField } from "./util"; import { isAllOnField } from "./util";
type MsgSelectUnselectCard = ygopro.StocGameMessage.MsgSelectUnselectCard; type MsgSelectUnselectCard = ygopro.StocGameMessage.MsgSelectUnselectCard;
export default async (selectUnselectCards: MsgSelectUnselectCard) => { export default async (
container: Container,
selectUnselectCards: MsgSelectUnselectCard,
) => {
const context = container.context;
const cardStore = context.cardStore;
const matStore = context.matStore;
const { const {
finishable, finishable,
cancelable, cancelable,
...@@ -48,12 +54,12 @@ export default async (selectUnselectCards: MsgSelectUnselectCard) => { ...@@ -48,12 +54,12 @@ export default async (selectUnselectCards: MsgSelectUnselectCard) => {
selecteds: selecteds1, selecteds: selecteds1,
mustSelects: mustSelect1, mustSelects: mustSelect1,
selectables: selectable1, selectables: selectable1,
} = await fetchCheckCardMeta(selectableCards); } = await fetchCheckCardMeta(context, selectableCards);
const { const {
selecteds: selecteds2, selecteds: selecteds2,
mustSelects: mustSelect2, mustSelects: mustSelect2,
selectables: selectable2, selectables: selectable2,
} = await fetchCheckCardMeta(selectedCards, true); } = await fetchCheckCardMeta(context, selectedCards, true);
await displaySelectActionsModal({ await displaySelectActionsModal({
finishable, finishable,
cancelable, cancelable,
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/stores"; import { Container } from "@/container";
export default (_set: ygopro.StocGameMessage.MsgSet) => { import { fetchEsHintMeta } from "./util";
fetchEsHintMeta({ originMsg: 1601 });
export default (container: Container, _set: ygopro.StocGameMessage.MsgSet) => {
fetchEsHintMeta({ context: container.context, originMsg: 1601 });
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { cardStore } from "@/stores";
export default (shuffleDeck: ygopro.StocGameMessage.MsgShuffleDeck) => { export default (
container: Container,
shuffleDeck: ygopro.StocGameMessage.MsgShuffleDeck,
) => {
const context = container.context;
playEffect(AudioActionType.SOUND_SHUFFLE); playEffect(AudioActionType.SOUND_SHUFFLE);
const player = shuffleDeck.player; const player = shuffleDeck.player;
for (const card of cardStore.at(ygopro.CardZone.DECK, player)) { for (const card of context.cardStore.at(ygopro.CardZone.DECK, player)) {
// 把数据抹掉就好了 // 把数据抹掉就好了
card.code = 0; card.code = 0;
card.meta = { id: 0, data: {}, text: {} }; card.meta = { id: 0, data: {}, text: {} };
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { cardStore } from "@/stores";
import { callCardMove } from "@/ui/Duel/PlayMat/Card"; import { callCardMove } from "@/ui/Duel/PlayMat/Card";
type MsgShuffleHandExtra = ygopro.StocGameMessage.MsgShuffleHandExtra; type MsgShuffleHandExtra = ygopro.StocGameMessage.MsgShuffleHandExtra;
export default async (shuffleHandExtra: MsgShuffleHandExtra) => { export default async (
container: Container,
shuffleHandExtra: MsgShuffleHandExtra,
) => {
const context = container.context;
playEffect(AudioActionType.SOUND_SHUFFLE); playEffect(AudioActionType.SOUND_SHUFFLE);
const { cards: codes, player: controller, zone } = shuffleHandExtra; const { cards: codes, player: controller, zone } = shuffleHandExtra;
// 本质上是要将手卡/额外卡组的sequence变成和codes一样的顺序 // 本质上是要将手卡/额外卡组的sequence变成和codes一样的顺序
const cards = cardStore.at(zone, controller); const cards = context.cardStore.at(zone, controller);
const hash = new Map(codes.map((code) => [code, new Array()])); const hash = new Map(codes.map((code) => [code, new Array()]));
codes.forEach((code, sequence) => { codes.forEach((code, sequence) => {
hash.get(code)?.push(sequence); hash.get(code)?.push(sequence);
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { cardStore } from "@/stores";
import { callCardMove } from "@/ui/Duel/PlayMat/Card"; import { callCardMove } from "@/ui/Duel/PlayMat/Card";
import MsgShuffleSetCard = ygopro.StocGameMessage.MsgShuffleSetCard; import MsgShuffleSetCard = ygopro.StocGameMessage.MsgShuffleSetCard;
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
// 后端传过来的`from_locations`的列表是切洗前场上卡的location,它们在列表里面按照切洗后的顺序排列 // 后端传过来的`from_locations`的列表是切洗前场上卡的location,它们在列表里面按照切洗后的顺序排列
export default async (shuffleSetCard: MsgShuffleSetCard) => { export default async (
container: Container,
shuffleSetCard: MsgShuffleSetCard,
) => {
playEffect(AudioActionType.SOUND_SHUFFLE); playEffect(AudioActionType.SOUND_SHUFFLE);
const context = container.context;
const from_locations = shuffleSetCard.from_locations; const from_locations = shuffleSetCard.from_locations;
const overlay_locations = shuffleSetCard.overlay_locations; const overlay_locations = shuffleSetCard.overlay_locations;
if (from_locations.length === 0) { if (from_locations.length === 0) {
...@@ -24,7 +28,11 @@ export default async (shuffleSetCard: MsgShuffleSetCard) => { ...@@ -24,7 +28,11 @@ export default async (shuffleSetCard: MsgShuffleSetCard) => {
Promise.all( Promise.all(
Array.from({ length: count }).map(async (_, i) => { Array.from({ length: count }).map(async (_, i) => {
const from = from_locations[i]; const from = from_locations[i];
const target = cardStore.at(from.zone, from.controller, from.sequence); const target = context.cardStore.at(
from.zone,
from.controller,
from.sequence,
);
if (target) { if (target) {
// 设置code为0,洗切后的code会由`UpdateData`指定 // 设置code为0,洗切后的code会由`UpdateData`指定
target.code = 0; target.code = 0;
...@@ -38,7 +46,7 @@ export default async (shuffleSetCard: MsgShuffleSetCard) => { ...@@ -38,7 +46,7 @@ export default async (shuffleSetCard: MsgShuffleSetCard) => {
const overlay_location = overlay_locations[i]; const overlay_location = overlay_locations[i];
if (overlay_location.zone > 0) { if (overlay_location.zone > 0) {
// 如果没有超量素材,后端会全传0 // 如果没有超量素材,后端会全传0
for (const overlay of cardStore.findOverlay( for (const overlay of context.cardStore.findOverlay(
from.zone, from.zone,
from.controller, from.controller,
from.sequence, from.sequence,
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { roomStore } from "@/stores"; import { Container } from "@/container";
type MsgSibylName = ygopro.StocGameMessage.MsgSibylName; type MsgSibylName = ygopro.StocGameMessage.MsgSibylName;
export default (sibylName: MsgSibylName) => { export default (container: Container, sibylName: MsgSibylName) => {
const me = roomStore.getMePlayer(); const context = container.context;
const op = roomStore.getOpPlayer(); const me = context.roomStore.getMePlayer();
const op = context.roomStore.getOpPlayer();
if (me) { if (me) {
me.name = sibylName.name_0; me.name = sibylName.name_0;
} }
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/stores"; import { Container } from "@/container";
export default (_: ygopro.StocGameMessage.MsgSpSummoned) => { import { fetchEsHintMeta } from "./util";
fetchEsHintMeta({ originMsg: 1606 });
export default (
container: Container,
_: ygopro.StocGameMessage.MsgSpSummoned,
) => {
fetchEsHintMeta({ context: container.context, originMsg: 1606 });
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/stores"; import { Container } from "@/container";
export default (spSummoning: ygopro.StocGameMessage.MsgSpSummoning) => { import { fetchEsHintMeta } from "./util";
export default (
container: Container,
spSummoning: ygopro.StocGameMessage.MsgSpSummoning,
) => {
// const card = fetchCard(spSummoning.code); // const card = fetchCard(spSummoning.code);
// if (card.data.type && card.data.type & TYPE_TOKEN) { // if (card.data.type && card.data.type & TYPE_TOKEN) {
// playEffect(AudioActionType.SOUND_TOKEN); // playEffect(AudioActionType.SOUND_TOKEN);
...@@ -9,6 +14,7 @@ export default (spSummoning: ygopro.StocGameMessage.MsgSpSummoning) => { ...@@ -9,6 +14,7 @@ export default (spSummoning: ygopro.StocGameMessage.MsgSpSummoning) => {
// playEffect(AudioActionType.SOUND_SPECIAL_SUMMON); // playEffect(AudioActionType.SOUND_SPECIAL_SUMMON);
// } // }
fetchEsHintMeta({ fetchEsHintMeta({
context: container.context,
originMsg: "「[?]」特殊召唤宣言时", originMsg: "「[?]」特殊召唤宣言时",
cardID: spSummoning.code, cardID: spSummoning.code,
}); });
......
...@@ -3,40 +3,37 @@ import { v4 as v4uuid } from "uuid"; ...@@ -3,40 +3,37 @@ import { v4 as v4uuid } from "uuid";
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import { Container } from "@/container";
import { sleep } from "@/infra"; import { sleep } from "@/infra";
import { import { replayStore, RoomStage, SideStage } from "@/stores";
cardStore,
matStore,
replayStore,
RoomStage,
roomStore,
SideStage,
sideStore,
} from "@/stores";
import { replayStart } from "@/ui/Match/ReplayModal"; import { replayStart } from "@/ui/Match/ReplayModal";
import { genCard } from "../utils"; import { genCard } from "../utils";
const TOKEN_SIZE = 13; // 每人场上最多就只可能有13个token const TOKEN_SIZE = 13; // 每人场上最多就只可能有13个token
export default async (start: ygopro.StocGameMessage.MsgStart) => { export default async (
container: Container,
start: ygopro.StocGameMessage.MsgStart,
) => {
const context = container.context;
// 先初始化`matStore` // 先初始化`matStore`
matStore.selfType = start.playerType; context.matStore.selfType = start.playerType;
if (sideStore.stage !== SideStage.NONE) { if (context.sideStore.stage !== SideStage.NONE) {
// 更新Side状态 // 更新Side状态
sideStore.stage = SideStage.DUEL_START; context.sideStore.stage = SideStage.DUEL_START;
} else { } else {
// 通知房间页面决斗开始 // 通知房间页面决斗开始
// 这行在该函数中的位置不能随便放,否则可能会block住 // 这行在该函数中的位置不能随便放,否则可能会block住
roomStore.stage = RoomStage.DUEL_START; context.roomStore.stage = RoomStage.DUEL_START;
} }
matStore.initInfo.set(0, { context.matStore.initInfo.set(0, {
life: start.life1, life: start.life1,
deckSize: start.deckSize1, deckSize: start.deckSize1,
extraSize: start.extraSize1, extraSize: start.extraSize1,
}); });
matStore.initInfo.set(1, { context.matStore.initInfo.set(1, {
life: start.life2, life: start.life2,
deckSize: start.deckSize2, deckSize: start.deckSize2,
extraSize: start.extraSize2, extraSize: start.extraSize2,
...@@ -85,7 +82,7 @@ export default async (start: ygopro.StocGameMessage.MsgStart) => { ...@@ -85,7 +82,7 @@ export default async (start: ygopro.StocGameMessage.MsgStart) => {
), ),
); );
cardStore.inner.push(...cards); context.cardStore.inner.push(...cards);
// note: 额外卡组的卡会在对局开始后通过`UpdateData` msg更新 // note: 额外卡组的卡会在对局开始后通过`UpdateData` msg更新
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/stores"; import { Container } from "@/container";
export default (_: ygopro.StocGameMessage.MsgSummoned) => { import { fetchEsHintMeta } from "./util";
fetchEsHintMeta({ originMsg: 1604 });
export default (
container: Container,
_: ygopro.StocGameMessage.MsgSummoned,
) => {
fetchEsHintMeta({ context: container.context, originMsg: 1604 });
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/stores"; import { Container } from "@/container";
export default (summoning: ygopro.StocGameMessage.MsgSummoning) => { import { fetchEsHintMeta } from "./util";
export default (
container: Container,
summoning: ygopro.StocGameMessage.MsgSummoning,
) => {
/* 因为现在Neos动画架构的问题,这里播放音效的话会滞后于移动动画, /* 因为现在Neos动画架构的问题,这里播放音效的话会滞后于移动动画,
* 因此这里先注释掉,等解决掉上述问题后再加上召唤的音效。 * 因此这里先注释掉,等解决掉上述问题后再加上召唤的音效。
* */ * */
// playEffect(AudioActionType.SOUND_SUMMON); // playEffect(AudioActionType.SOUND_SUMMON);
fetchEsHintMeta({ fetchEsHintMeta({
context: container.context,
originMsg: "「[?]」通常召唤宣言时", originMsg: "「[?]」通常召唤宣言时",
cardID: summoning.code, cardID: summoning.code,
}); });
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/stores"; import { Container } from "@/container";
export default (_swap: ygopro.StocGameMessage.MsgSwap) => { import { fetchEsHintMeta } from "./util";
fetchEsHintMeta({ originMsg: 1602 });
export default (
container: Container,
_swap: ygopro.StocGameMessage.MsgSwap,
) => {
fetchEsHintMeta({ context: container.context, originMsg: 1602 });
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { cardStore } from "@/stores";
import { callCardMove } from "@/ui/Duel/PlayMat/Card"; import { callCardMove } from "@/ui/Duel/PlayMat/Card";
import MsgSwapGraveDeck = ygopro.StocGameMessage.MsgSwapGraveDeck; import MsgSwapGraveDeck = ygopro.StocGameMessage.MsgSwapGraveDeck;
import { Container } from "@/container";
const { DECK, GRAVE } = ygopro.CardZone; const { DECK, GRAVE } = ygopro.CardZone;
export default async (swapGraveDeck: MsgSwapGraveDeck) => { export default async (
container: Container,
swapGraveDeck: MsgSwapGraveDeck,
) => {
const context = container.context;
const player = swapGraveDeck.player; const player = swapGraveDeck.player;
const deck = cardStore.at(DECK, player); const deck = context.cardStore.at(DECK, player);
const grave = cardStore.at(GRAVE, player); const grave = context.cardStore.at(GRAVE, player);
for (const card of deck) { for (const card of deck) {
card.location.zone = GRAVE; card.location.zone = GRAVE;
......
import { sendTimeConfirm, ygopro } from "@/api"; import { sendTimeConfirm, ygopro } from "@/api";
import { Container } from "@/container"; import { Container } from "@/container";
import { matStore } from "@/stores";
export default function handleTimeLimit( export default function handleTimeLimit(
container: Container, container: Container,
timeLimit: ygopro.StocTimeLimit, timeLimit: ygopro.StocTimeLimit,
) { ) {
matStore.timeLimits.set(timeLimit.player, timeLimit.left_time); const context = container.context;
if (matStore.isMe(timeLimit.player)) { context.matStore.timeLimits.set(timeLimit.player, timeLimit.left_time);
if (context.matStore.isMe(timeLimit.player)) {
sendTimeConfirm(container.conn); sendTimeConfirm(container.conn);
} }
} }
import { fetchStrings, Region, ygopro } from "@/api"; import { fetchStrings, Region, ygopro } from "@/api";
import { sleep } from "@/infra"; import { sleep } from "@/infra";
import { matStore } from "@/stores";
import MsgToss = ygopro.StocGameMessage.MsgToss; import MsgToss = ygopro.StocGameMessage.MsgToss;
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
export default async (toss: MsgToss) => { export default async (container: Container, toss: MsgToss) => {
const context = container.context;
const player = toss.player; const player = toss.player;
const tossType = toss.toss_type; const tossType = toss.toss_type;
const prefix = fetchStrings(Region.System, matStore.isMe(player) ? 102 : 103); const prefix = fetchStrings(
Region.System,
context.matStore.isMe(player) ? 102 : 103,
);
for (const x of toss.res) { for (const x of toss.res) {
if (tossType === MsgToss.TossType.DICE) { if (tossType === MsgToss.TossType.DICE) {
playEffect(AudioActionType.SOUND_DICE); playEffect(AudioActionType.SOUND_DICE);
matStore.tossResult = prefix + fetchStrings(Region.System, 1624) + x; context.matStore.tossResult =
prefix + fetchStrings(Region.System, 1624) + x;
} else if (tossType === MsgToss.TossType.COIN) { } else if (tossType === MsgToss.TossType.COIN) {
playEffect(AudioActionType.SOUND_COIN); playEffect(AudioActionType.SOUND_COIN);
matStore.tossResult = context.matStore.tossResult =
prefix + prefix +
fetchStrings(Region.System, 1623) + fetchStrings(Region.System, 1623) +
fetchStrings(Region.System, 61 - x); fetchStrings(Region.System, 61 - x);
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import { matStore } from "@/stores"; import { Container } from "@/container";
const NeosConfig = useConfig(); const NeosConfig = useConfig();
export default (unimplemented: ygopro.StocGameMessage.MsgUnimplemented) => { export default (
container: Container,
unimplemented: ygopro.StocGameMessage.MsgUnimplemented,
) => {
if (!NeosConfig.unimplementedWhiteList.includes(unimplemented.command)) { if (!NeosConfig.unimplementedWhiteList.includes(unimplemented.command)) {
matStore.unimplemented = unimplemented.command; container.context.matStore.unimplemented = unimplemented.command;
} }
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { cardStore } from "@/stores";
type MsgUpdateCounter = ygopro.StocGameMessage.MsgUpdateCounter; type MsgUpdateCounter = ygopro.StocGameMessage.MsgUpdateCounter;
export default (updateCounter: MsgUpdateCounter) => { export default (container: Container, updateCounter: MsgUpdateCounter) => {
const context = container.context;
const { location, count, action_type: counterType } = updateCounter; const { location, count, action_type: counterType } = updateCounter;
const target = cardStore.find(location); // 不太确定这个后面能不能相应,我不好说 const target = context.cardStore.find(location); // 不太确定这个后面能不能相应,我不好说
if (target) { if (target) {
switch (counterType) { switch (counterType) {
case ygopro.StocGameMessage.MsgUpdateCounter.ActionType.ADD: { case ygopro.StocGameMessage.MsgUpdateCounter.ActionType.ADD: {
......
import { fetchCard, ygopro } from "@/api"; import { fetchCard, ygopro } from "@/api";
import { cardStore } from "@/stores";
import { callCardMove } from "@/ui/Duel/PlayMat/Card"; import { callCardMove } from "@/ui/Duel/PlayMat/Card";
import MsgUpdateData = ygopro.StocGameMessage.MsgUpdateData; import MsgUpdateData = ygopro.StocGameMessage.MsgUpdateData;
import { TYPE_TOKEN } from "@/common"; import { TYPE_TOKEN } from "@/common";
import { Container } from "@/container";
export default async (updateData: MsgUpdateData) => { export default async (container: Container, updateData: MsgUpdateData) => {
const { player: controller, zone, actions } = updateData; const { player: controller, zone, actions } = updateData;
if (controller !== undefined && zone !== undefined && actions !== undefined) { if (controller !== undefined && zone !== undefined && actions !== undefined) {
const field = cardStore.at(zone, controller); const field = container.context.cardStore.at(zone, controller);
for (const action of actions) { for (const action of actions) {
const sequence = action.location?.sequence; const sequence = action.location?.sequence;
if (typeof sequence !== "undefined") { if (typeof sequence !== "undefined") {
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta, matStore } from "@/stores";
import MsgUpdateHp = ygopro.StocGameMessage.MsgUpdateHp; import MsgUpdateHp = ygopro.StocGameMessage.MsgUpdateHp;
import { Container } from "@/container";
import { AudioActionType, changeScene, playEffect } from "@/infra/audio"; import { AudioActionType, changeScene, playEffect } from "@/infra/audio";
export default (msgUpdateHp: MsgUpdateHp) => { import { fetchEsHintMeta } from "./util";
export default (container: Container, msgUpdateHp: MsgUpdateHp) => {
const context = container.context;
if (msgUpdateHp.type_ === MsgUpdateHp.ActionType.DAMAGE) { if (msgUpdateHp.type_ === MsgUpdateHp.ActionType.DAMAGE) {
playEffect(AudioActionType.SOUND_DAMAGE); playEffect(AudioActionType.SOUND_DAMAGE);
fetchEsHintMeta({ originMsg: "玩家收到伤害时" }); // TODO: i18n fetchEsHintMeta({ context, originMsg: "玩家收到伤害时" }); // TODO: i18n
matStore.initInfo.of(msgUpdateHp.player).life -= msgUpdateHp.value; context.matStore.initInfo.of(msgUpdateHp.player).life -= msgUpdateHp.value;
} else if (msgUpdateHp.type_ === MsgUpdateHp.ActionType.RECOVER) { } else if (msgUpdateHp.type_ === MsgUpdateHp.ActionType.RECOVER) {
playEffect(AudioActionType.SOUND_RECOVER); playEffect(AudioActionType.SOUND_RECOVER);
fetchEsHintMeta({ originMsg: "玩家生命值回复时" }); // TODO: i18n fetchEsHintMeta({ context, originMsg: "玩家生命值回复时" }); // TODO: i18n
matStore.initInfo.of(msgUpdateHp.player).life += msgUpdateHp.value; context.matStore.initInfo.of(msgUpdateHp.player).life += msgUpdateHp.value;
} }
if (matStore.initInfo.me.life > matStore.initInfo.op.life * 2) { if (
context.matStore.initInfo.me.life >
context.matStore.initInfo.op.life * 2
) {
changeScene(AudioActionType.BGM_ADVANTAGE); changeScene(AudioActionType.BGM_ADVANTAGE);
} }
if (matStore.initInfo.me.life * 2 < matStore.initInfo.op.life) { if (
context.matStore.initInfo.me.life * 2 <
context.matStore.initInfo.op.life
) {
changeScene(AudioActionType.BGM_DISADVANTAGE); changeScene(AudioActionType.BGM_DISADVANTAGE);
} }
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
const { MZONE, SZONE, HAND } = ygopro.CardZone; const { MZONE, SZONE, HAND } = ygopro.CardZone;
import { fetchStrings } from "@/api";
import { Region } from "@/api";
import { fetchCard } from "@/api/cards";
import { Context } from "@/container";
export function isAllOnField(locations: ygopro.CardLocation[]): boolean { export function isAllOnField(locations: ygopro.CardLocation[]): boolean {
const isOnField = (location: ygopro.CardLocation) => { const isOnField = (location: ygopro.CardLocation) => {
...@@ -53,3 +57,41 @@ export function argmax<T>(arr: T[], getValue: (item: T) => number): number { ...@@ -53,3 +57,41 @@ export function argmax<T>(arr: T[], getValue: (item: T) => number): number {
return maxIndex; return maxIndex;
} }
export const fetchEsHintMeta = async ({
context,
originMsg,
location,
cardID,
}: {
context: Context;
originMsg: string | number;
location?: ygopro.CardLocation;
cardID?: number;
}) => {
const newOriginMsg =
typeof originMsg === "string"
? originMsg
: fetchStrings(Region.System, originMsg);
const cardMeta = cardID ? fetchCard(cardID) : undefined;
let esHint = newOriginMsg;
if (cardMeta?.text.name) {
esHint = esHint.replace("[?]", cardMeta.text.name);
}
if (location) {
const fieldMeta = context.cardStore.at(
location.zone,
location.controller,
location.sequence,
);
if (fieldMeta?.meta.text.name) {
esHint = esHint.replace("[?]", fieldMeta.meta.text.name);
}
}
context.matStore.hint.esHint = esHint;
};
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { cardStore } from "@/stores"; import { Container } from "@/container";
import { showWaiting } from "@/ui/Duel/Message"; import { showWaiting } from "@/ui/Duel/Message";
export default (_wait: ygopro.StocGameMessage.MsgWait) => { export default (
for (const card of cardStore.inner) { container: Container,
_wait: ygopro.StocGameMessage.MsgWait,
) => {
for (const card of container.context.cardStore.inner) {
card.idleInteractivities = []; card.idleInteractivities = [];
} }
showWaiting(true); showWaiting(true);
......
import { fetchStrings, Region, ygopro } from "@/api"; import { fetchStrings, Region, ygopro } from "@/api";
import { matStore } from "@/stores";
import { displayEndModal } from "@/ui/Duel/Message"; import { displayEndModal } from "@/ui/Duel/Message";
import MsgWin = ygopro.StocGameMessage.MsgWin; import MsgWin = ygopro.StocGameMessage.MsgWin;
import { Container } from "@/container";
import { AudioActionType, changeScene } from "@/infra/audio"; import { AudioActionType, changeScene } from "@/infra/audio";
export default async (win: MsgWin) => { export default async (container: Container, win: MsgWin) => {
const context = container.context;
const { win_player, reason } = win; const { win_player, reason } = win;
await displayEndModal( await displayEndModal(
matStore.isMe(win_player), context.matStore.isMe(win_player),
fetchStrings(Region.Victory, `0x${reason.toString(16)}`), fetchStrings(Region.Victory, `0x${reason.toString(16)}`),
); );
if (matStore.isMe(win_player)) { if (context.matStore.isMe(win_player)) {
changeScene(AudioActionType.BGM_WIN); changeScene(AudioActionType.BGM_WIN);
} else { } else {
changeScene(AudioActionType.BGM_LOSE); changeScene(AudioActionType.BGM_LOSE);
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { roomStore } from "@/stores"; import { Container } from "@/container";
// TODO: 这里设置的player可能顺序会反 // TODO: 这里设置的player可能顺序会反
export default function handleDeckCount(pb: ygopro.YgoStocMsg) { export default function handleDeckCount(
container: Container,
pb: ygopro.YgoStocMsg,
) {
const context = container.context;
const deckCount = pb.stoc_deck_count; const deckCount = pb.stoc_deck_count;
const me = roomStore.getMePlayer(); const me = context.roomStore.getMePlayer();
const op = roomStore.getOpPlayer(); const op = context.roomStore.getOpPlayer();
if (me) { if (me) {
me.deckInfo = { me.deckInfo = {
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { eventbus, Task } from "@/infra"; import { eventbus, Task } from "@/infra";
import { RoomStage, roomStore } from "@/stores"; import { RoomStage } from "@/stores";
export default function handleSelectHand(_: ygopro.YgoStocMsg) { export default function handleSelectHand(
roomStore.stage = RoomStage.HAND_SELECTING; container: Container,
_: ygopro.YgoStocMsg,
) {
const context = container.context;
context.roomStore.stage = RoomStage.HAND_SELECTING;
eventbus.emit(Task.Mora); eventbus.emit(Task.Mora);
} }
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { eventbus, Task } from "@/infra"; import { eventbus, Task } from "@/infra";
import { RoomStage, roomStore, SideStage, sideStore } from "@/stores"; import { RoomStage, SideStage } from "@/stores";
export default function handleSelectTp(_: ygopro.YgoStocMsg) { export default function handleSelectTp(
if (sideStore.stage !== SideStage.NONE) { container: Container,
sideStore.stage = SideStage.TP_SELECTING; _: ygopro.YgoStocMsg,
) {
const context = container.context;
if (context.sideStore.stage !== SideStage.NONE) {
context.sideStore.stage = SideStage.TP_SELECTING;
} else { } else {
roomStore.stage = RoomStage.TP_SELECTING; context.roomStore.stage = RoomStage.TP_SELECTING;
eventbus.emit(Task.Tp); eventbus.emit(Task.Tp);
} }
} }
...@@ -56,51 +56,51 @@ async function _handle( ...@@ -56,51 +56,51 @@ async function _handle(
switch (pb.msg) { switch (pb.msg) {
case "stoc_join_game": { case "stoc_join_game": {
handleJoinGame(pb); handleJoinGame(container, pb);
break; break;
} }
case "stoc_chat": { case "stoc_chat": {
handleChat(pb); handleChat(container, pb);
break; break;
} }
case "stoc_hs_player_change": { case "stoc_hs_player_change": {
handleHsPlayerChange(pb); handleHsPlayerChange(container, pb);
break; break;
} }
case "stoc_hs_watch_change": { case "stoc_hs_watch_change": {
handleHsWatchChange(pb); handleHsWatchChange(container, pb);
break; break;
} }
case "stoc_hs_player_enter": { case "stoc_hs_player_enter": {
handleHsPlayerEnter(pb); handleHsPlayerEnter(container, pb);
break; break;
} }
case "stoc_type_change": { case "stoc_type_change": {
handleTypeChange(pb); handleTypeChange(container, pb);
break; break;
} }
case "stoc_select_hand": { case "stoc_select_hand": {
handleSelectHand(pb); handleSelectHand(container, pb);
break; break;
} }
case "stoc_hand_result": { case "stoc_hand_result": {
handleHandResult(pb); handleHandResult(container, pb);
break; break;
} }
case "stoc_select_tp": { case "stoc_select_tp": {
handleSelectTp(pb); handleSelectTp(container, pb);
break; break;
} }
case "stoc_deck_count": { case "stoc_deck_count": {
handleDeckCount(pb); handleDeckCount(container, pb);
break; break;
} }
case "stoc_duel_start": { case "stoc_duel_start": {
handleDuelStart(pb); handleDuelStart(container, pb);
break; break;
} }
case "stoc_duel_end": { case "stoc_duel_end": {
handleDuelEnd(pb); handleDuelEnd(container, pb);
break; break;
} }
case "stoc_game_msg": { case "stoc_game_msg": {
...@@ -117,15 +117,15 @@ async function _handle( ...@@ -117,15 +117,15 @@ async function _handle(
break; break;
} }
case "stoc_error_msg": { case "stoc_error_msg": {
await handleErrorMsg(pb.stoc_error_msg); await handleErrorMsg(container, pb.stoc_error_msg);
break; break;
} }
case "stoc_change_side": { case "stoc_change_side": {
handleChangeSide(pb.stoc_change_side); handleChangeSide(container, pb.stoc_change_side);
break; break;
} }
case "stoc_waiting_side": { case "stoc_waiting_side": {
handleWaitingSide(pb.stoc_waiting_side); handleWaitingSide(container, pb.stoc_waiting_side);
break; break;
} }
default: { default: {
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { chatStore } from "@/stores";
export default function handleChat(pb: ygopro.YgoStocMsg) { export default function handleChat(
container: Container,
pb: ygopro.YgoStocMsg,
) {
playEffect(AudioActionType.SOUND_CHAT); playEffect(AudioActionType.SOUND_CHAT);
const context = container.context;
const chat = pb.stoc_chat; const chat = pb.stoc_chat;
chatStore.message = chat.msg; context.chatStore.message = chat.msg;
chatStore.sender = chat.player; context.chatStore.sender = chat.player;
} }
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { matStore } from "@/stores"; import { Container } from "@/container";
export default function handleDuelEnd(_pb: ygopro.YgoStocMsg) { export default function handleDuelEnd(
matStore.duelEnd = true; container: Container,
_pb: ygopro.YgoStocMsg,
) {
container.context.matStore.duelEnd = true;
} }
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { RoomStage, roomStore, SideStage, sideStore } from "@/stores"; import { Container } from "@/container";
import { RoomStage, SideStage } from "@/stores";
export default function handleDuelStart(_pb: ygopro.YgoStocMsg) { export default function handleDuelStart(
if (sideStore.stage !== SideStage.NONE) { container: Container,
sideStore.stage = SideStage.SIDE_CHANGED; _pb: ygopro.YgoStocMsg,
) {
const context = container.context;
if (context.sideStore.stage !== SideStage.NONE) {
context.sideStore.stage = SideStage.SIDE_CHANGED;
} else { } else {
roomStore.stage = RoomStage.MORA; context.roomStore.stage = RoomStage.MORA;
} }
} }
import { fetchCard, fetchStrings, Region, ygopro } from "@/api"; import { fetchCard, fetchStrings, Region, ygopro } from "@/api";
import { roomStore } from "@/stores";
import ErrorType = ygopro.StocErrorMsg.ErrorType; import ErrorType = ygopro.StocErrorMsg.ErrorType;
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
// TODO: 是时候需要一个统一管理国际化文案的模块了 // TODO: 是时候需要一个统一管理国际化文案的模块了
...@@ -64,10 +64,15 @@ const messages: Record< ...@@ -64,10 +64,15 @@ const messages: Record<
}; };
/* End of definition (I18N) */ /* End of definition (I18N) */
export default async function handleErrorMsg(errorMsg: ygopro.StocErrorMsg) { export default async function handleErrorMsg(
container: Container,
errorMsg: ygopro.StocErrorMsg,
) {
const { error_type, error_code } = errorMsg; const { error_type, error_code } = errorMsg;
playEffect(AudioActionType.SOUND_INFO); playEffect(AudioActionType.SOUND_INFO);
const roomStore = container.context.roomStore;
// Get the language from localStorage or default to 'cn' (I18N) // Get the language from localStorage or default to 'cn' (I18N)
const language = (localStorage.getItem("language") || "cn") as Language; const language = (localStorage.getItem("language") || "cn") as Language;
const mainDeckWarning = messages[language].mainDeckWarning; const mainDeckWarning = messages[language].mainDeckWarning;
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { roomStore } from "@/stores"; import { Container } from "@/container";
export default function handResult(pb: ygopro.YgoStocMsg) { export default function handResult(
container: Container,
pb: ygopro.YgoStocMsg,
) {
const msg = pb.stoc_hand_result; const msg = pb.stoc_hand_result;
const me = roomStore.getMePlayer(); const context = container.context;
const op = roomStore.getOpPlayer(); const me = context.roomStore.getMePlayer();
const op = context.roomStore.getOpPlayer();
if (me && op) { if (me && op) {
me.moraResult = msg.meResult; me.moraResult = msg.meResult;
op.moraResult = msg.opResult; op.moraResult = msg.opResult;
} else if (roomStore.selfType !== ygopro.StocTypeChange.SelfType.OBSERVER) { } else if (
context.roomStore.selfType !== ygopro.StocTypeChange.SelfType.OBSERVER
) {
console.error("<HandResult>me or op is undefined"); console.error("<HandResult>me or op is undefined");
} }
} }
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { roomStore } from "@/stores"; import { Container } from "@/container";
export default function handleHsPlayerChange(pb: ygopro.YgoStocMsg) { export default function handleHsPlayerChange(
container: Container,
pb: ygopro.YgoStocMsg,
) {
const change = pb.stoc_hs_player_change; const change = pb.stoc_hs_player_change;
const context = container.context;
if (change.pos > 1) { if (change.pos > 1) {
console.log("Currently only supported 2v2 mode."); console.log("Currently only supported 2v2 mode.");
...@@ -21,25 +25,26 @@ export default function handleHsPlayerChange(pb: ygopro.YgoStocMsg) { ...@@ -21,25 +25,26 @@ export default function handleHsPlayerChange(pb: ygopro.YgoStocMsg) {
" moved to " + " moved to " +
change.moved_pos, change.moved_pos,
); );
roomStore.players[change.moved_pos] = roomStore.players[change.pos]; context.roomStore.players[change.moved_pos] =
roomStore.players[change.pos] = undefined; context.roomStore.players[change.pos];
context.roomStore.players[change.pos] = undefined;
break; break;
} }
case ygopro.StocHsPlayerChange.State.READY: case ygopro.StocHsPlayerChange.State.READY:
case ygopro.StocHsPlayerChange.State.NO_READY: { case ygopro.StocHsPlayerChange.State.NO_READY: {
const player = roomStore.players[change.pos]; const player = context.roomStore.players[change.pos];
if (player) { if (player) {
player.state = change.state; player.state = change.state;
} }
break; break;
} }
case ygopro.StocHsPlayerChange.State.LEAVE: { case ygopro.StocHsPlayerChange.State.LEAVE: {
roomStore.players[change.pos] = undefined; context.roomStore.players[change.pos] = undefined;
break; break;
} }
case ygopro.StocHsPlayerChange.State.TO_OBSERVER: { case ygopro.StocHsPlayerChange.State.TO_OBSERVER: {
roomStore.players[change.pos] = undefined; context.roomStore.players[change.pos] = undefined;
roomStore.observerCount += 1; context.roomStore.observerCount += 1;
break; break;
} }
default: { default: {
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { Container } from "@/container";
import { AudioActionType, playEffect } from "@/infra/audio"; import { AudioActionType, playEffect } from "@/infra/audio";
import { roomStore } from "@/stores";
export default function handleHsPlayerEnter(pb: ygopro.YgoStocMsg) { export default function handleHsPlayerEnter(
container: Container,
pb: ygopro.YgoStocMsg,
) {
playEffect(AudioActionType.SOUND_PLAYER_ENTER); playEffect(AudioActionType.SOUND_PLAYER_ENTER);
const name = pb.stoc_hs_player_enter.name; const name = pb.stoc_hs_player_enter.name;
const pos = pb.stoc_hs_player_enter.pos; const pos = pb.stoc_hs_player_enter.pos;
const context = container.context;
const player = roomStore.players[pos]; const player = context.roomStore.players[pos];
if (player) { if (player) {
player.name = name; player.name = name;
} else { } else {
roomStore.players[pos] = { context.roomStore.players[pos] = {
name, name,
state: ygopro.StocHsPlayerChange.State.NO_READY, state: ygopro.StocHsPlayerChange.State.NO_READY,
}; };
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { roomStore } from "@/stores"; import { Container } from "@/container";
export default function handleHsWatchChange(pb: ygopro.YgoStocMsg) { export default function handleHsWatchChange(
container: Container,
pb: ygopro.YgoStocMsg,
) {
const count = pb.stoc_hs_watch_change.count; const count = pb.stoc_hs_watch_change.count;
roomStore.observerCount = count; container.context.roomStore.observerCount = count;
} }
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { roomStore } from "@/stores"; import { Container } from "@/container";
export default function handleJoinGame(pb: ygopro.YgoStocMsg) { export default function handleJoinGame(
container: Container,
pb: ygopro.YgoStocMsg,
) {
const _msg = pb.stoc_join_game; const _msg = pb.stoc_join_game;
// TODO container.context.roomStore.joined = true;
roomStore.joined = true;
} }
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { roomStore } from "@/stores";
import SelfType = ygopro.StocTypeChange.SelfType; import SelfType = ygopro.StocTypeChange.SelfType;
import { Container } from "@/container";
export default function handleTypeChange(pb: ygopro.YgoStocMsg) { export default function handleTypeChange(
container: Container,
pb: ygopro.YgoStocMsg,
) {
const selfType = pb.stoc_type_change.self_type; const selfType = pb.stoc_type_change.self_type;
const assertHost = pb.stoc_type_change.is_host; const assertHost = pb.stoc_type_change.is_host;
const context = container.context;
roomStore.isHost = assertHost; context.roomStore.isHost = assertHost;
roomStore.selfType = selfType; context.roomStore.selfType = selfType;
switch (selfType) { switch (selfType) {
case SelfType.UNKNOWN: { case SelfType.UNKNOWN: {
...@@ -15,7 +19,7 @@ export default function handleTypeChange(pb: ygopro.YgoStocMsg) { ...@@ -15,7 +19,7 @@ export default function handleTypeChange(pb: ygopro.YgoStocMsg) {
break; break;
} }
case SelfType.OBSERVER: { case SelfType.OBSERVER: {
roomStore.players.forEach((player) => { context.roomStore.players.forEach((player) => {
if (player) { if (player) {
player.isMe = false; player.isMe = false;
} }
...@@ -23,13 +27,17 @@ export default function handleTypeChange(pb: ygopro.YgoStocMsg) { ...@@ -23,13 +27,17 @@ export default function handleTypeChange(pb: ygopro.YgoStocMsg) {
break; break;
} }
default: { default: {
const player = roomStore.players[selfType - 1]; const player = context.roomStore.players[selfType - 1];
const state = ygopro.StocHsPlayerChange.State.NO_READY; const state = ygopro.StocHsPlayerChange.State.NO_READY;
if (player) { if (player) {
player.state = state; player.state = state;
player.isMe = true; player.isMe = true;
} else { } else {
roomStore.players[selfType - 1] = { name: "?", state, isMe: true }; context.roomStore.players[selfType - 1] = {
name: "?",
state,
isMe: true,
};
} }
break; break;
} }
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { SideStage, sideStore } from "@/stores"; import { Container } from "@/container";
export function handleChangeSide(_: ygopro.StocChangeSide) { import { SideStage } from "@/stores";
sideStore.stage = SideStage.SIDE_CHANGING; export function handleChangeSide(
container: Container,
_: ygopro.StocChangeSide,
) {
container.context.sideStore.stage = SideStage.SIDE_CHANGING;
} }
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { SideStage, sideStore } from "@/stores"; import { Container } from "@/container";
export function handleWaitingSide(_: ygopro.StocWaitingSide) { import { SideStage } from "@/stores";
sideStore.stage = SideStage.WAITING; export function handleWaitingSide(
container: Container,
_: ygopro.StocWaitingSide,
) {
container.context.sideStore.stage = SideStage.WAITING;
} }
import type { ygopro } from "@/api"; import type { ygopro } from "@/api";
import { fetchCard, getCardStr } from "@/api/cards"; import { fetchCard, getCardStr } from "@/api/cards";
import { cardStore } from "@/stores"; import { Context } from "@/container";
import type { Option } from "@/ui/Duel/Message"; import type { Option } from "@/ui/Duel/Message";
const helper = async ( const helper = async (
context: Context,
{ {
code, code,
location, location,
...@@ -26,7 +27,7 @@ const helper = async ( ...@@ -26,7 +27,7 @@ const helper = async (
mustSelect?: boolean, mustSelect?: boolean,
) => { ) => {
const { controller, zone, sequence } = location; const { controller, zone, sequence } = location;
const target = cardStore.at(zone, controller, sequence); const target = context.cardStore.at(zone, controller, sequence);
// 这里可能直接用target.meta即可,不用再查一遍DB // 这里可能直接用target.meta即可,不用再查一遍DB
// 但是ygopro后端传回来了code,感觉这里会有些坑,因此求稳这样写 // 但是ygopro后端传回来了code,感觉这里会有些坑,因此求稳这样写
...@@ -63,6 +64,7 @@ const helper = async ( ...@@ -63,6 +64,7 @@ const helper = async (
}; };
export const fetchCheckCardMeta = async ( export const fetchCheckCardMeta = async (
context: Context,
cards: { cards: {
code: number; code: number;
location: ygopro.CardLocation; location: ygopro.CardLocation;
...@@ -79,6 +81,7 @@ export const fetchCheckCardMeta = async ( ...@@ -79,6 +81,7 @@ export const fetchCheckCardMeta = async (
const selectables: Option[] = []; const selectables: Option[] = [];
for (const card of cards) { for (const card of cards) {
await helper( await helper(
context,
card, card,
selecteds, selecteds,
mustSelects, mustSelects,
......
...@@ -94,6 +94,3 @@ export class CardStore implements NeosStore { ...@@ -94,6 +94,3 @@ export class CardStore implements NeosStore {
} }
export const cardStore = proxy(new CardStore()); export const cardStore = proxy(new CardStore());
// @ts-ignore
window.cardStore = cardStore;
import { Region, type ygopro } from "@/api"; import { Region } from "@/api";
import { DESCRIPTION_LIMIT, fetchStrings, getStrings } from "@/api"; import { DESCRIPTION_LIMIT, fetchStrings, getStrings } from "@/api";
import { fetchCard } from "@/api/cards"; import { fetchCard } from "@/api/cards";
import { cardStore } from "@/stores/cardStore";
import { matStore } from "../store"; import { matStore } from "../store";
...@@ -38,39 +37,3 @@ export const fetchSelectHintMeta = ({ ...@@ -38,39 +37,3 @@ export const fetchSelectHintMeta = ({
matStore.hint.esHint = esHint; matStore.hint.esHint = esHint;
} }
}; };
export const fetchEsHintMeta = async ({
originMsg,
location,
cardID,
}: {
originMsg: string | number;
location?: ygopro.CardLocation;
cardID?: number;
}) => {
const newOriginMsg =
typeof originMsg === "string"
? originMsg
: fetchStrings(Region.System, originMsg);
const cardMeta = cardID ? fetchCard(cardID) : undefined;
let esHint = newOriginMsg;
if (cardMeta?.text.name) {
esHint = esHint.replace("[?]", cardMeta.text.name);
}
if (location) {
const fieldMeta = cardStore.at(
location.zone,
location.controller,
location.sequence,
);
if (fieldMeta?.meta.text.name) {
esHint = esHint.replace("[?]", fieldMeta.meta.text.name);
}
}
matStore.hint.esHint = esHint;
};
...@@ -2,7 +2,7 @@ import { cloneDeep } from "lodash-es"; ...@@ -2,7 +2,7 @@ import { cloneDeep } from "lodash-es";
import { proxy } from "valtio"; import { proxy } from "valtio";
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { matStore } from "@/stores"; import { Context } from "@/container";
import type { Interactivity } from "./matStore/types"; import type { Interactivity } from "./matStore/types";
import { type NeosStore } from "./shared"; import { type NeosStore } from "./shared";
...@@ -65,14 +65,16 @@ export class PlaceStore implements NeosStore { ...@@ -65,14 +65,16 @@ export class PlaceStore implements NeosStore {
op: BlockState[]; op: BlockState[];
}; };
} = initialState; } = initialState;
of(location: { of(
context: Context,
location: {
zone: ygopro.CardZone; zone: ygopro.CardZone;
controller: number; controller: number;
sequence: number; sequence: number;
}): BlockState | undefined { },
): BlockState | undefined {
return this.inner[location.zone][ return this.inner[location.zone][
// FIXME: inject `matStore` context.matStore.isMe(location.controller) ? "me" : "op"
matStore.isMe(location.controller) ? "me" : "op"
][location.sequence]; ][location.sequence];
} }
clearAllInteractivity() { clearAllInteractivity() {
......
...@@ -6,6 +6,7 @@ import { useSnapshot } from "valtio"; ...@@ -6,6 +6,7 @@ import { useSnapshot } from "valtio";
import { sendChat } from "@/api"; import { sendChat } from "@/api";
import { getUIContainer } from "@/container/compat"; import { getUIContainer } from "@/container/compat";
// TODO: access store via `Container`
import { chatStore, isMe, roomStore } from "@/stores"; import { chatStore, isMe, roomStore } from "@/stores";
interface ChatItem { interface ChatItem {
......
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