Commit d2b01b1b authored by timel's avatar timel

refactor: use class to implement store, with edited prototype chain

parent c2e315c9
...@@ -7,7 +7,7 @@ import { fetchCard } from "@/api/cards"; ...@@ -7,7 +7,7 @@ import { fetchCard } from "@/api/cards";
import type { import type {
BothSide, BothSide,
CardState, CardState,
DuelFieldState, DuelFieldState as ArrayCardState,
InitInfo, InitInfo,
MatState, MatState,
} from "./types"; } from "./types";
...@@ -20,105 +20,113 @@ import { InteractType } from "./types"; ...@@ -20,105 +20,113 @@ import { InteractType } from "./types";
const getWhom = (controller: number): "me" | "op" => const getWhom = (controller: number): "me" | "op" =>
isMe(controller) ? "me" : "op"; isMe(controller) ? "me" : "op";
/** /** 卡的列表,提供了一些方便的方法 */
* 根据自己的先后手判断是否是自己 class CardArray extends Array<CardState> implements ArrayCardState {
* 原本名字叫judgeSelf public __proto__ = CardArray.prototype;
*/ public zone: ygopro.CardZone = ygopro.CardZone.MZONE;
const isMe = (controller: number): boolean => { public getController: () => number = () => 1;
switch (matStore.selfType) { private genCard = async (controller: number, id: number) => ({
case 1:
// 自己是先攻
return controller === 0;
case 2:
// 自己是后攻
return controller === 1;
default:
// 目前不可能出现这种情况
console.error("judgeSelf error", controller, matStore.selfType);
return false;
}
};
const genDuel = <T extends {}>(meObj: T, opObj?: T): BothSide<T> => {
// 提供opObj是为了让meObj和opObj的类型可以不同,避免深拷贝的坑...
const res = proxy({
me: Object.assign(meObj, {
getController: () => (matStore.selfType == 1 ? 0 : 1),
}),
op: Object.assign(opObj ?? meObj, {
getController: () => (matStore.selfType == 1 ? 0 : 1),
}),
of: (controller: number) => res[getWhom(controller)],
});
return res;
};
const addMethods = <T extends CardState[]>(
entity: T,
zone: ygopro.CardZone
): DuelFieldState => {
/** 生成一个卡片,根据`id`获取卡片信息 */
const genCard = async (controller: number, id: number) => ({
occupant: await fetchCard(id, true), occupant: await fetchCard(id, true),
location: { location: {
controler: controller, controler: controller,
location: zone, location: this.zone,
}, },
counters: {}, counters: {},
idleInteractivities: [], idleInteractivities: [],
}); });
// methods
const res = proxy(entity) as unknown as DuelFieldState; remove(sequence: number) {
res.remove = (sequence: number) => { this.splice(sequence, 1);
res.splice(sequence, 1); }
}; async insert(sequence: number, id: number) {
res.insert = async (sequence: number, id: number) => { const card = await this.genCard(this.getController(), id);
const card = await genCard(res.getController(), id); this.splice(sequence, 0, card);
res.splice(sequence, 0, card); }
}; async add(ids: number[]) {
res.add = async (ids: number[]) => {
const cards = await Promise.all( const cards = await Promise.all(
ids.map(async (id) => genCard(res.getController(), id)) ids.map(async (id) => this.genCard(this.getController(), id))
); );
res.splice(res.length, 0, ...cards); this.splice(this.length, 0, ...cards);
}; }
res.setOccupant = async ( async setOccupant(
sequence: number, sequence: number,
id: number, id: number,
position?: ygopro.CardPosition position?: ygopro.CardPosition
) => { ) {
const meta = await fetchCard(id); const meta = await fetchCard(id);
const target = res[sequence]; const target = this[sequence];
target.occupant = meta; target.occupant = meta;
if (position) { if (position) {
target.location.position = position; target.location.position = position;
} }
}; }
addIdleInteractivity(
res.addIdleInteractivity = (
sequence: number, sequence: number,
interactivity: CardState["idleInteractivities"][number] interactivity: CardState["idleInteractivities"][number]
) => { ) {
res[sequence].idleInteractivities.push(interactivity); this[sequence].idleInteractivities.push(interactivity);
}; }
res.clearIdleInteractivities = () => { clearIdleInteractivities() {
res.forEach((card) => (card.idleInteractivities = [])); this.forEach((card) => (card.idleInteractivities = []));
}; }
res.setPlaceInteractivityType = ( setPlaceInteractivityType(sequence: number, interactType: InteractType) {
sequence: number, this[sequence].placeInteractivity = {
interactType: InteractType
) => {
res[sequence].placeInteractivity = {
interactType: interactType, interactType: interactType,
response: { response: {
controler: res.getController(), controler: this.getController(),
zone, zone: this.zone,
sequence, sequence,
}, },
}; };
}; }
res.clearPlaceInteractivity = () => { clearPlaceInteractivity() {
res.forEach((card) => (card.placeInteractivity = undefined)); this.forEach((card) => (card.placeInteractivity = undefined));
}
}
const genDuelCardArray = (cardStates: CardState[], zone: ygopro.CardZone) => {
const me = new CardArray(...cardStates);
me.zone = zone;
me.getController = () => (matStore.selfType == 1 ? 0 : 1);
const op = new CardArray(...cardStates);
op.zone = zone;
op.getController = () => (matStore.selfType == 1 ? 1 : 0);
const res = proxy({
me,
op,
of: (controller: number) => {
res[getWhom(controller)].__proto__ = CardArray.prototype;
return res[getWhom(controller)];
},
});
return res;
};
/**
* 根据自己的先后手判断是否是自己
* 原本名字叫judgeSelf
*/
const isMe = (controller: number): boolean => {
switch (matStore.selfType) {
case 1:
// 自己是先攻
return controller === 0;
case 2:
// 自己是后攻
return controller === 1;
default:
// 目前不可能出现这种情况
console.error("judgeSelf error", controller, matStore.selfType);
return false;
}
};
const genDuelNormal = <T extends {}>(meObj: T): BothSide<T> => {
// 提供opObj是为了让meObj和opObj的类型可以不同,避免深拷贝的坑...
const res = {
me: { ...meObj },
op: { ...meObj },
of: (controller: number) => res[getWhom(controller)],
}; };
return res; return res;
}; };
...@@ -138,7 +146,7 @@ const genBlock = (location: ygopro.CardZone, n: number) => ...@@ -138,7 +146,7 @@ const genBlock = (location: ygopro.CardZone, n: number) =>
})); }));
const initInfo: MatState["initInfo"] = proxy({ const initInfo: MatState["initInfo"] = proxy({
...genDuel({ ...genDuelNormal({
masterRule: "UNKNOWN", masterRule: "UNKNOWN",
life: -1, // 特地设置一个不可能的值 life: -1, // 特地设置一个不可能的值
deckSize: 0, deckSize: 0,
...@@ -188,23 +196,18 @@ const { SZONE, MZONE, GRAVE, REMOVED, HAND, DECK, EXTRA } = ygopro.CardZone; ...@@ -188,23 +196,18 @@ const { SZONE, MZONE, GRAVE, REMOVED, HAND, DECK, EXTRA } = ygopro.CardZone;
* 具体介绍可以点进`MatState`去看 * 具体介绍可以点进`MatState`去看
*/ */
export const matStore: MatState = proxy<MatState>({ export const matStore: MatState = proxy<MatState>({
magics: genDuel( magics: genDuelCardArray(genBlock(SZONE, 6), SZONE),
addMethods(genBlock(SZONE, 6), SZONE), monsters: genDuelCardArray(genBlock(MZONE, 7), MZONE),
addMethods(genBlock(SZONE, 6), SZONE) graveyards: genDuelCardArray([], GRAVE),
), banishedZones: genDuelCardArray([], REMOVED),
monsters: genDuel( hands: genDuelCardArray([], HAND),
addMethods(genBlock(MZONE, 7), MZONE), decks: genDuelCardArray([], DECK),
addMethods(genBlock(MZONE, 7), MZONE) extraDecks: genDuelCardArray([], EXTRA),
),
graveyards: genDuel(addMethods([], GRAVE), addMethods([], GRAVE)),
banishedZones: genDuel(addMethods([], REMOVED), addMethods([], REMOVED)),
hands: genDuel(addMethods([], HAND), addMethods([], HAND)),
decks: genDuel(addMethods([], DECK), addMethods([], DECK)),
extraDecks: genDuel(addMethods([], EXTRA), addMethods([], EXTRA)),
timeLimits: { timeLimits: {
// 时间限制 // 时间限制
...genDuel(-1), ...genDuelNormal(-1),
of: (controller: number) => matStore.timeLimits[getWhom(controller)],
set: (controller: number, time: number) => { set: (controller: number, time: number) => {
matStore.timeLimits[getWhom(controller)] = time; matStore.timeLimits[getWhom(controller)] = time;
}, },
...@@ -229,5 +232,24 @@ export const matStore: MatState = proxy<MatState>({ ...@@ -229,5 +232,24 @@ export const matStore: MatState = proxy<MatState>({
isMe, isMe,
}); });
// 以后再来解决这些...
// @ts-ignore // @ts-ignore
window.matStore = matStore; window.matStore = matStore;
// 修改原型链,因为valtiol的proxy会把原型链改掉。这应该是valtio的一个bug...有空提issue去改
(["me", "op"] as const).forEach((who) => {
(
[
"hands",
"decks",
"extraDecks",
"graveyards",
"banishedZones",
"monsters",
"magics",
] as const
).forEach((zone) => {
matStore[zone][who].__proto__ = CardArray.prototype;
});
});
...@@ -12,8 +12,7 @@ export interface BothSide<T> { ...@@ -12,8 +12,7 @@ export interface BothSide<T> {
/** /**
* CardState的顺序index,被称为sequence * CardState的顺序index,被称为sequence
*/ */
export type DuelFieldState = CardState[] & { export interface DuelFieldState extends Array<CardState> {
getController: () => number;
/** 移除特定位置的卡片 */ /** 移除特定位置的卡片 */
remove: (sequence: number) => void; remove: (sequence: number) => void;
/** 在末尾添加卡片 */ /** 在末尾添加卡片 */
...@@ -40,7 +39,10 @@ export type DuelFieldState = CardState[] & { ...@@ -40,7 +39,10 @@ export type DuelFieldState = CardState[] & {
) => void; ) => void;
/** 移除 place 的交互性 */ /** 移除 place 的交互性 */
clearPlaceInteractivity: () => void; clearPlaceInteractivity: () => void;
};
// 让原型链不报错
__proto__?: DuelFieldState;
}
type test = DuelFieldState extends (infer S)[] ? S : never; type test = DuelFieldState extends (infer S)[] ? S : never;
......
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