Commit fdecfd97 authored by timel's avatar timel

feat: valtio store logic 30%

parent ab716aa8
...@@ -66,6 +66,9 @@ export async function fetchCard( ...@@ -66,6 +66,9 @@ export async function fetchCard(
return res.data; return res.data;
} }
// 挂到全局 以便 debug
window.fetchCard = fetchCard;
export function getCardStr(meta: CardMeta, idx: number): string | undefined { export function getCardStr(meta: CardMeta, idx: number): string | undefined {
switch (idx) { switch (idx) {
case 0: { case 0: {
......
...@@ -3,10 +3,16 @@ import { fetchHandsMeta } from "@/reducers/duel/handsSlice"; ...@@ -3,10 +3,16 @@ import { fetchHandsMeta } from "@/reducers/duel/handsSlice";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice"; import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store"; import { AppDispatch } from "@/store";
import { valtioStore } from "@/valtioStores";
export default ( export default (
draw: ygopro.StocGameMessage.MsgDraw, draw: ygopro.StocGameMessage.MsgDraw,
dispatch: AppDispatch dispatch: AppDispatch
) => { ) => {
dispatch(fetchEsHintMeta({ originMsg: "玩家抽卡时" })); dispatch(fetchEsHintMeta({ originMsg: "玩家抽卡时" }));
dispatch(fetchHandsMeta({ controler: draw.player, codes: draw.cards })); dispatch(fetchHandsMeta({ controler: draw.player, codes: draw.cards }));
const playMat = valtioStore.duelStore.playMat;
playMat.hands.add(draw.player, draw.cards);
}; };
...@@ -20,8 +20,12 @@ import { ...@@ -20,8 +20,12 @@ import {
} from "@/reducers/duel/monstersSlice"; } from "@/reducers/duel/monstersSlice";
import { AppDispatch } from "@/store"; import { AppDispatch } from "@/store";
import { valtioStore } from "@/valtioStores";
import { REASON_MATERIAL } from "../../common"; import { REASON_MATERIAL } from "../../common";
const { playMat: playMatStore } = valtioStore.duelStore;
const OVERLAY_STACK: { code: number; sequence: number }[] = []; const OVERLAY_STACK: { code: number; sequence: number }[] = [];
export default (move: MsgMove, dispatch: AppDispatch) => { export default (move: MsgMove, dispatch: AppDispatch) => {
...@@ -33,7 +37,7 @@ export default (move: MsgMove, dispatch: AppDispatch) => { ...@@ -33,7 +37,7 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
switch (from.location) { switch (from.location) {
case ygopro.CardZone.HAND: { case ygopro.CardZone.HAND: {
dispatch(removeHand([from.controler, from.sequence])); dispatch(removeHand([from.controler, from.sequence]));
playMatStore.hands.remove(from.controler, from.sequence);
break; break;
} }
case ygopro.CardZone.MZONE: { case ygopro.CardZone.MZONE: {
...@@ -144,6 +148,7 @@ export default (move: MsgMove, dispatch: AppDispatch) => { ...@@ -144,6 +148,7 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
dispatch( dispatch(
insertHandMeta({ controler: to.controler, sequence: to.sequence, code }) insertHandMeta({ controler: to.controler, sequence: to.sequence, code })
); );
playMatStore.hands.insert(to.controler, to.sequence, code);
break; break;
} }
......
...@@ -11,11 +11,28 @@ import { ...@@ -11,11 +11,28 @@ import {
} from "@/reducers/duel/mod"; } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store"; import { AppDispatch } from "@/store";
import { valtioStore } from "@/valtioStores";
const playMatStore = valtioStore.duelStore.playMat;
export default ( export default (
start: ygopro.StocGameMessage.MsgStart, start: ygopro.StocGameMessage.MsgStart,
dispatch: AppDispatch dispatch: AppDispatch
) => { ) => {
dispatch(setSelfType(start.playerType)); dispatch(setSelfType(start.playerType));
playMatStore.selfType = start.playerType;
playMatStore.initInfo.set(0, {
life: start.life1,
deckSize: start.deckSize1,
extraSize: start.extraSize1,
});
playMatStore.initInfo.set(1, {
life: start.life2,
deckSize: start.deckSize2,
extraSize: start.extraSize2,
});
dispatch( dispatch(
infoInit([ infoInit([
0, 0,
......
import { proxy } from "valtio"; import { proxy } from "valtio";
import { ygopro } from "@/api/ocgcore/idl/ocgcore"; import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { fetchCard } from "@/api/cards";
import type { PlayMatState } from "./types"; import type {
PlayMatState,
DuelFieldState,
CardsBothSide,
BothSide,
InitInfo,
} from "./types";
/** /**
* 生成一个指定长度的卡片数组 * 生成一个指定长度的卡片数组
*/ */
function genDuelFieldState(location: ygopro.CardZone, n: number = 5) { function genBlock(location: ygopro.CardZone, n: number = 5) {
return { return {
inner: Array(n) me: Array(n)
.fill(null)
.map((_) => ({
location: {
location,
},
idleInteractivities: [],
counters: {},
})),
op: Array(n)
.fill(null) .fill(null)
.map((_) => ({ .map((_) => ({
location: { location: {
...@@ -21,36 +36,86 @@ function genDuelFieldState(location: ygopro.CardZone, n: number = 5) { ...@@ -21,36 +36,86 @@ function genDuelFieldState(location: ygopro.CardZone, n: number = 5) {
}; };
} }
export const playMat = proxy<PlayMatState>({ const initInfo: PlayMatState["initInfo"] = proxy({
opMagics: genDuelFieldState(ygopro.CardZone.SZONE), me: {
meMagics: genDuelFieldState(ygopro.CardZone.SZONE),
opMonsters: genDuelFieldState(ygopro.CardZone.MZONE),
meMonsters: genDuelFieldState(ygopro.CardZone.MZONE),
opGraveyard: { inner: [] },
meGraveyard: { inner: [] },
opBanishedZone: { inner: [] },
meBanishedZone: { inner: [] },
opHands: { inner: [] },
meHands: { inner: [] },
opDeck: { inner: [] },
meDeck: { inner: [] },
opExtraDeck: { inner: [] },
meExtraDeck: { inner: [] },
selfType: ygopro.StocTypeChange.SelfType.UNKNOWN,
meInitInfo: {
masterRule: "UNKNOWN", masterRule: "UNKNOWN",
life: 7999, // 特地设置一个不可能的值 life: -1, // 特地设置一个不可能的值
deckSize: 0, deckSize: 0,
extraSize: 0, extraSize: 0,
}, },
opInitInfo: { op: {
masterRule: "", masterRule: "UNKNOWN",
life: 7999, // 特地设置一个不可能的值 life: -1, // 特地设置一个不可能的值
deckSize: 0, deckSize: 0,
extraSize: 0, extraSize: 0,
}, },
set: (controller: number, obj: Partial<InitInfo>) => {
initInfo[getWhom(controller)] = {
...initInfo[getWhom(controller)],
...obj,
};
},
});
/**
* 给 `{me: [...], op: [...]}` 这种类型的对象添加一些方法
*/
const wrap = <T extends DuelFieldState>(
entity: BothSide<T>,
zone: ygopro.CardZone
): CardsBothSide<T> => {
/**
* 生成一个卡片,根据`id`获取卡片信息
*/
const genCard = async (controller: number, id: number) => ({
occupant: await fetchCard(id, true),
location: {
controler: controller,
location: zone,
},
counters: {},
idleInteractivities: [],
});
const res: CardsBothSide<T> = proxy({
...entity,
remove: (controller: number, sequence: number) => {
res[getWhom(controller)].splice(sequence, 1);
},
insert: async (controller: number, sequence: number, id: number) => {
const card = await genCard(controller, id);
res[getWhom(controller)].splice(sequence, 0, card);
},
add: async (controller: number, ids: number[]) => {
const cards = await Promise.all(
ids.map(async (id) => genCard(controller, id))
);
res[getWhom(controller)].splice(
res[getWhom(controller)].length,
0,
...cards
);
},
});
return res;
};
export const playMat = proxy<PlayMatState>({
magics: wrap(genBlock(ygopro.CardZone.SZONE), ygopro.CardZone.SZONE),
monsters: wrap(genBlock(ygopro.CardZone.MZONE), ygopro.CardZone.MZONE),
graveyard: wrap({ me: [], op: [] }, ygopro.CardZone.GRAVE),
banishedZone: wrap({ me: [], op: [] }, ygopro.CardZone.REMOVED),
hands: wrap({ me: [], op: [] }, ygopro.CardZone.HAND),
deck: wrap({ me: [], op: [] }, ygopro.CardZone.DECK),
extraDeck: wrap({ me: [], op: [] }, ygopro.CardZone.EXTRA),
initInfo,
timeLimit: {
me: 0,
op: 0,
},
selfType: ygopro.StocTypeChange.SelfType.UNKNOWN,
hint: { hint: {
code: -1, code: -1,
}, },
...@@ -65,3 +130,21 @@ export const playMat = proxy<PlayMatState>({ ...@@ -65,3 +130,21 @@ export const playMat = proxy<PlayMatState>({
waiting: false, waiting: false,
unimplemented: 0, unimplemented: 0,
}); });
const getWhom = (controller: number) =>
judgeSelf(controller, playMat.selfType) ? "me" : "op";
export function judgeSelf(player: number, selfType: number): boolean {
switch (selfType) {
case 1:
// 自己是先攻
return player === 0;
case 2:
// 自己是后攻
return player === 1;
default:
// 目前不可能出现这种情况
console.error("judgeSelf error", player, selfType);
return false;
}
}
...@@ -2,34 +2,40 @@ import type { CardMeta } from "@/api/cards"; ...@@ -2,34 +2,40 @@ import type { CardMeta } from "@/api/cards";
import type { ygopro } from "@/api/ocgcore/idl/ocgcore"; import type { ygopro } from "@/api/ocgcore/idl/ocgcore";
// >>> play mat state >>> // >>> play mat state >>>
export type BothSide<T> = {
me: T;
op: T;
};
export interface CardsBothSide<T extends DuelFieldState> extends BothSide<T> {
remove: (player: number, sequence: number) => void; // 移除特定位置的卡片
add: (controller: number, ids: number[]) => void; // 在末尾添加卡片
insert: (controller: number, sequence: number, id: number) => void; // 在指定位置插入卡片
}
export interface PlayMatState { export interface PlayMatState {
selfType: number; selfType: number;
meInitInfo: InitInfo; // 自己的初始状态
opInitInfo: InitInfo; // 对手的初始状态
meHands: HandState; // 自己的手牌 initInfo: BothSide<InitInfo> & {
opHands: HandState; // 对手的手牌 set: (controller: number, obj: Partial<InitInfo>) => void;
}; // 双方的初始化信息
meMonsters: MonsterState; // 自己的怪兽区状态 hands: CardsBothSide<HandState>; // 双方的手牌
opMonsters: MonsterState; // 对手的怪兽区状态
meMagics: MagicState; // 自己的魔法陷阱区状态 monsters: CardsBothSide<MonsterState>; // 双方的怪兽区状态
opMagics: MagicState; // 对手的魔法陷阱区状态
meGraveyard: GraveyardState; // 自己的墓地状态 magics: CardsBothSide<MagicState>; // 双方的魔法区状态
opGraveyard: GraveyardState; // 对手的墓地状态
meBanishedZone: BanishedZoneState; // 自己的除外区状态 graveyard: CardsBothSide<GraveyardState>; // 双方的墓地状态
opBanishedZone: BanishedZoneState; // 对手的除外区状态
meDeck: DeckState; // 自己的卡组状态 banishedZone: CardsBothSide<BanishedZoneState>; // 双方的除外区状态
opDeck: DeckState; // 对手的卡组状态
meExtraDeck: ExtraDeckState; // 自己的额外卡组状态 deck: CardsBothSide<DeckState>; // 双方的卡组状态
opExtraDeck: ExtraDeckState; // 对手的额外卡组状态
meTimeLimit?: TimeLimit; // 自己的计时 extraDeck: CardsBothSide<ExtraDeckState>; // 双方的额外卡组状态
opTimeLimit?: TimeLimit; // 对手的计时
timeLimit: BothSide<number>; // 双方的时间限制
hint: HintState; hint: HintState;
...@@ -42,6 +48,8 @@ export interface PlayMatState { ...@@ -42,6 +48,8 @@ export interface PlayMatState {
waiting: boolean; waiting: boolean;
unimplemented: number; // 未处理的`Message` unimplemented: number; // 未处理的`Message`
// remove: (player: number, sequence: number) => void;
} }
export interface InitInfo { export interface InitInfo {
...@@ -51,14 +59,18 @@ export interface InitInfo { ...@@ -51,14 +59,18 @@ export interface InitInfo {
extraSize: number; extraSize: number;
} }
/**
* 场上某位置的状态,
* 以后会更名为 BlockState
*/
export interface CardState { export interface CardState {
occupant?: CardMeta; // 占据此位置的卡牌元信息 occupant?: CardMeta; // 占据此位置的卡牌元信息
location: { location: {
controler?: number; controler?: number; // 控制这个位置的玩家,0或1
location: ygopro.CardZone; location: ygopro.CardZone; // 怪兽区/魔法陷阱区/手牌/卡组/墓地/除外区
position?: ygopro.CardPosition; position?: ygopro.CardPosition; // 卡片的姿势:攻击还是守备
overlay_sequence?: number; overlay_sequence?: number;
}; // 位置信息 }; // 位置信息,叫location的原因是为了和ygo对齐
idleInteractivities: Interactivity<number>[]; // IDLE状态下的互动信息 idleInteractivities: Interactivity<number>[]; // IDLE状态下的互动信息
placeInteractivities?: Interactivity<{ placeInteractivities?: Interactivity<{
controler: number; controler: number;
...@@ -70,9 +82,10 @@ export interface CardState { ...@@ -70,9 +82,10 @@ export interface CardState {
reload?: boolean; // 这个字段会在收到MSG_RELOAD_FIELD的时候设置成true,在收到MSG_UPDATE_DATE的时候设置成false reload?: boolean; // 这个字段会在收到MSG_RELOAD_FIELD的时候设置成true,在收到MSG_UPDATE_DATE的时候设置成false
} }
export interface DuelFieldState { /**
inner: CardState[]; * CardState的顺序index,被称为sequence
} */
export type DuelFieldState = CardState[];
export interface Interactivity<T> { export interface Interactivity<T> {
interactType: InteractType; interactType: InteractType;
......
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