Commit 01992c4f authored by Chunchi Che's avatar Chunchi Che

Merge branch 'refactor/store' into 'main'

refactor store

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