Commit fdecfd97 authored by timel's avatar timel

feat: valtio store logic 30%

parent ab716aa8
......@@ -66,6 +66,9 @@ export async function fetchCard(
return res.data;
}
// 挂到全局 以便 debug
window.fetchCard = fetchCard;
export function getCardStr(meta: CardMeta, idx: number): string | undefined {
switch (idx) {
case 0: {
......
......@@ -3,10 +3,16 @@ import { fetchHandsMeta } from "@/reducers/duel/handsSlice";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import { valtioStore } from "@/valtioStores";
export default (
draw: ygopro.StocGameMessage.MsgDraw,
dispatch: AppDispatch
) => {
dispatch(fetchEsHintMeta({ originMsg: "玩家抽卡时" }));
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 {
} from "@/reducers/duel/monstersSlice";
import { AppDispatch } from "@/store";
import { valtioStore } from "@/valtioStores";
import { REASON_MATERIAL } from "../../common";
const { playMat: playMatStore } = valtioStore.duelStore;
const OVERLAY_STACK: { code: number; sequence: number }[] = [];
export default (move: MsgMove, dispatch: AppDispatch) => {
......@@ -33,7 +37,7 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
switch (from.location) {
case ygopro.CardZone.HAND: {
dispatch(removeHand([from.controler, from.sequence]));
playMatStore.hands.remove(from.controler, from.sequence);
break;
}
case ygopro.CardZone.MZONE: {
......@@ -144,6 +148,7 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
dispatch(
insertHandMeta({ controler: to.controler, sequence: to.sequence, code })
);
playMatStore.hands.insert(to.controler, to.sequence, code);
break;
}
......
......@@ -11,11 +11,28 @@ import {
} from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import { valtioStore } from "@/valtioStores";
const playMatStore = valtioStore.duelStore.playMat;
export default (
start: ygopro.StocGameMessage.MsgStart,
dispatch: AppDispatch
) => {
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(
infoInit([
0,
......
import { proxy } from "valtio";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import type { PlayMatState } from "./types";
import { fetchCard } from "@/api/cards";
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 {
inner: Array(n)
me: Array(n)
.fill(null)
.map((_) => ({
location: {
location,
},
idleInteractivities: [],
counters: {},
})),
op: Array(n)
.fill(null)
.map((_) => ({
location: {
......@@ -21,36 +36,86 @@ function genDuelFieldState(location: ygopro.CardZone, n: number = 5) {
};
}
export const playMat = proxy<PlayMatState>({
opMagics: genDuelFieldState(ygopro.CardZone.SZONE),
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: {
const initInfo: PlayMatState["initInfo"] = proxy({
me: {
masterRule: "UNKNOWN",
life: 7999, // 特地设置一个不可能的值
life: -1, // 特地设置一个不可能的值
deckSize: 0,
extraSize: 0,
},
opInitInfo: {
masterRule: "",
life: 7999, // 特地设置一个不可能的值
op: {
masterRule: "UNKNOWN",
life: -1, // 特地设置一个不可能的值
deckSize: 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: {
code: -1,
},
......@@ -65,3 +130,21 @@ export const playMat = proxy<PlayMatState>({
waiting: false,
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";
import type { ygopro } from "@/api/ocgcore/idl/ocgcore";
// >>> 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 {
selfType: number;
meInitInfo: InitInfo; // 自己的初始状态
opInitInfo: InitInfo; // 对手的初始状态
meHands: HandState; // 自己的手牌
opHands: HandState; // 对手的手牌
initInfo: BothSide<InitInfo> & {
set: (controller: number, obj: Partial<InitInfo>) => void;
}; // 双方的初始化信息
meMonsters: MonsterState; // 自己的怪兽区状态
opMonsters: MonsterState; // 对手的怪兽区状态
hands: CardsBothSide<HandState>; // 双方的手牌
meMagics: MagicState; // 自己的魔法陷阱区状态
opMagics: MagicState; // 对手的魔法陷阱区状态
monsters: CardsBothSide<MonsterState>; // 双方的怪兽区状态
meGraveyard: GraveyardState; // 自己的墓地状态
opGraveyard: GraveyardState; // 对手的墓地状态
magics: CardsBothSide<MagicState>; // 双方的魔法区状态
meBanishedZone: BanishedZoneState; // 自己的除外区状态
opBanishedZone: BanishedZoneState; // 对手的除外区状态
graveyard: CardsBothSide<GraveyardState>; // 双方的墓地状态
meDeck: DeckState; // 自己的卡组状态
opDeck: DeckState; // 对手的卡组状态
banishedZone: CardsBothSide<BanishedZoneState>; // 双方的除外区状态
meExtraDeck: ExtraDeckState; // 自己的额外卡组状态
opExtraDeck: ExtraDeckState; // 对手的额外卡组状态
deck: CardsBothSide<DeckState>; // 双方的卡组状态
meTimeLimit?: TimeLimit; // 自己的计时
opTimeLimit?: TimeLimit; // 对手的计时
extraDeck: CardsBothSide<ExtraDeckState>; // 双方的额外卡组状态
timeLimit: BothSide<number>; // 双方的时间限制
hint: HintState;
......@@ -42,6 +48,8 @@ export interface PlayMatState {
waiting: boolean;
unimplemented: number; // 未处理的`Message`
// remove: (player: number, sequence: number) => void;
}
export interface InitInfo {
......@@ -51,14 +59,18 @@ export interface InitInfo {
extraSize: number;
}
/**
* 场上某位置的状态,
* 以后会更名为 BlockState
*/
export interface CardState {
occupant?: CardMeta; // 占据此位置的卡牌元信息
location: {
controler?: number;
location: ygopro.CardZone;
position?: ygopro.CardPosition;
controler?: number; // 控制这个位置的玩家,0或1
location: ygopro.CardZone; // 怪兽区/魔法陷阱区/手牌/卡组/墓地/除外区
position?: ygopro.CardPosition; // 卡片的姿势:攻击还是守备
overlay_sequence?: number;
}; // 位置信息
}; // 位置信息,叫location的原因是为了和ygo对齐
idleInteractivities: Interactivity<number>[]; // IDLE状态下的互动信息
placeInteractivities?: Interactivity<{
controler: number;
......@@ -70,9 +82,10 @@ export interface CardState {
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> {
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