Commit 67d5bd0d authored by timel's avatar timel

feat: valtio store logic 100%, ui/mat

parent 0ee5f4d1
......@@ -10,3 +10,10 @@ interface ImportMetaEnv {
interface ImportMeta {
readonly env: ImportMetaEnv;
}
// 重新声明useSnapshot,暂时先这么写。原版的会把所有的改成readonly,引发一些棘手的类型报错。
import "valtio/react";
declare module "valtio/react" {
export declare function useSnapshot<T extends object>(proxyObject: T): T;
export {};
}
......@@ -16,6 +16,7 @@ import { store } from "@/store";
import {
messageStore,
matStore,
clearAllIdleInteractivities as FIXME_clearAllIdleInteractivities,
} from "@/valtioStores";
import { useSnapshot } from "valtio";
......@@ -44,7 +45,6 @@ export const CardListModal = () => {
<Drawer open={isOpen} onClose={handleOkOrCancel}>
<List
itemLayout="horizontal"
// @ts-ignore 报错是因为类型不可变,实际上是没问题的
dataSource={list}
renderItem={(item) => (
<List.Item
......
......@@ -10,20 +10,27 @@ import {
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
export const BanishedZone = () => {
const meBanishedZone = useAppSelector(selectMeBanishedZone).inner;
const opBanishedZone = useAppSelector(selectOpBanishedZone).inner;
// const meBanishedZone = useAppSelector(selectMeBanishedZone).inner;
// const opBanishedZone = useAppSelector(selectOpBanishedZone).inner;
const meBanishedZone = useSnapshot(matStore.banishedZones.me);
const opBanishedZone = useSnapshot(matStore.banishedZones.op);
return (
<>
<SingleSlot
state={meBanishedZone}
// 因为singleSlot里面会有snap,所以这儿可以直接传入store
state={matStore.banishedZones.me}
position={banishedZonePosition(0, meBanishedZone.length)}
rotation={cardSlotRotation(false)}
/>
<SingleSlot
state={opBanishedZone}
state={matStore.banishedZones.op}
position={banishedZonePosition(1, opBanishedZone.length)}
rotation={cardSlotRotation(true)}
/>
......
......@@ -8,19 +8,26 @@ import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
const NeosConfig = useConfig();
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
export const CommonDeck = () => {
const meDeck = useAppSelector(selectMeDeck).inner;
const opDeck = useAppSelector(selectOpDeck).inner;
// const meDeck = useAppSelector(selectMeDeck).inner;
// const opDeck = useAppSelector(selectOpDeck).inner;
const meDeck = useSnapshot(matStore.decks.me);
const opDeck = useSnapshot(matStore.decks.op);
return (
<>
<SingleSlot
state={meDeck}
state={matStore.decks.me}
position={deckPosition(0, meDeck.length)}
rotation={cardSlotRotation(false)}
/>
<SingleSlot
state={opDeck}
state={matStore.decks.op}
position={deckPosition(1, opDeck.length)}
rotation={cardSlotRotation(true)}
/>
......
......@@ -12,19 +12,24 @@ import { Depth, SingleSlot } from "./SingleSlot";
const NeosConfig = useConfig();
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
export const ExtraDeck = () => {
const meExtraDeck = useAppSelector(selectMeExtraDeck).inner;
const opExtraDeck = useAppSelector(selectOpExtraDeck).inner;
// const meExtraDeck = useAppSelector(selectMeExtraDeck).inner;
// const opExtraDeck = useAppSelector(selectOpExtraDeck).inner;
const meExtraDeck = useSnapshot(matStore.extraDecks.me);
const opExtraDeck = useSnapshot(matStore.extraDecks.op);
return (
<>
<SingleSlot
state={meExtraDeck}
state={matStore.extraDecks.me}
position={extraDeckPosition(0, meExtraDeck.length)}
rotation={cardSlotRotation(false)}
/>
<SingleSlot
state={opExtraDeck}
state={matStore.extraDecks.op}
position={extraDeckPosition(1, opExtraDeck.length)}
rotation={cardSlotRotation(true)}
/>
......
......@@ -10,34 +10,47 @@ import { FixedSlot } from "./FixedSlot";
import { Depth } from "./SingleSlot";
const NeosConfig = useConfig();
import { matStore, clearAllPlaceInteradtivities } from "@/valtioStores";
import { useSnapshot } from "valtio";
export const Field = () => {
const meField = useAppSelector(selectMeMagics).inner.find(
(_, sequence) => sequence == 5
);
const opField = useAppSelector(selectOpMagics).inner.find(
(_, sequence) => sequence == 5
);
// const meField = useAppSelector(selectMeMagics).inner.find(
// (_, sequence) => sequence == 5
// );
// const opField = useAppSelector(selectOpMagics).inner.find(
// (_, sequence) => sequence == 5
// );
// 这儿的find可能是出于某种考虑,以后再深思
const meField = useSnapshot(matStore.magics.me[5]);
const opField = useSnapshot(matStore.magics.op[5]);
const clearPlaceInteractivitiesAction = (controller: number) =>
matStore.magics.of(controller).clearPlaceInteractivity();
return (
<>
{meField ? (
<FixedSlot
state={meField}
snapState={meField}
sequence={0}
position={fieldPosition(0)}
rotation={cardSlotRotation(false)}
clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
// clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
) : (
<></>
)}
{opField ? (
<FixedSlot
state={opField}
snapState={opField}
sequence={0}
position={fieldPosition(1)}
rotation={cardSlotRotation(true)}
clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
// clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
) : (
<></>
......
......@@ -18,6 +18,9 @@ import {
import { store } from "@/store";
import { interactTypeToString } from "../utils";
import { useSnapshot } from "valtio";
import { clearAllIdleInteractivities } from "@/valtioStores";
const NeosConfig = useConfig();
......@@ -30,19 +33,22 @@ const cardDefenceRotation = new BABYLON.Vector3(
);
export const FixedSlot = (props: {
state: CardState;
snapState: CardState;
sequence: number;
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
deffenseRotation?: BABYLON.Vector3;
clearPlaceInteractivitiesAction: ActionCreatorWithPayload<number, string>;
// clearPlaceInteractivitiesAction: ActionCreatorWithPayload<number, string>;
clearPlaceInteractivitiesAction: (controller: number) => void;
}) => {
const planeRef = useRef(null);
// const snapState = useSnapshot(props.state);
const snapState = props.snapState;
const rotation =
props.state.location.position === ygopro.CardPosition.DEFENSE ||
props.state.location.position === ygopro.CardPosition.FACEUP_DEFENSE ||
props.state.location.position === ygopro.CardPosition.FACEDOWN_DEFENSE
snapState.location.position === ygopro.CardPosition.DEFENSE ||
snapState.location.position === ygopro.CardPosition.FACEUP_DEFENSE ||
snapState.location.position === ygopro.CardPosition.FACEDOWN_DEFENSE
? props.deffenseRotation || cardDefenceRotation
: props.rotation;
const edgesWidth = 2.0;
......@@ -50,22 +56,24 @@ export const FixedSlot = (props: {
const dispatch = store.dispatch;
const faceDown =
props.state.location.position === ygopro.CardPosition.FACEDOWN_DEFENSE ||
props.state.location.position === ygopro.CardPosition.FACEDOWN_ATTACK ||
props.state.location.position === ygopro.CardPosition.FACEDOWN;
snapState.location.position === ygopro.CardPosition.FACEDOWN_DEFENSE ||
snapState.location.position === ygopro.CardPosition.FACEDOWN_ATTACK ||
snapState.location.position === ygopro.CardPosition.FACEDOWN;
useClick(
(_event) => {
if (props.state.placeInteractivities) {
sendSelectPlaceResponse(props.state.placeInteractivities.response);
dispatch(props.clearPlaceInteractivitiesAction(0));
dispatch(props.clearPlaceInteractivitiesAction(1));
} else if (props.state.occupant) {
if (snapState.placeInteractivities) {
sendSelectPlaceResponse(snapState.placeInteractivities.response);
// dispatch(props.clearPlaceInteractivitiesAction(0));
// dispatch(props.clearPlaceInteractivitiesAction(1));
clearAllIdleInteractivities(0);
clearAllIdleInteractivities(1);
} else if (snapState.occupant) {
// 中央弹窗展示选中卡牌信息
dispatch(setCardModalMeta(props.state.occupant));
dispatch(setCardModalMeta(snapState.occupant));
dispatch(
setCardModalInteractivies(
props.state.idleInteractivities.map((interactivity) => {
snapState.idleInteractivities.map((interactivity) => {
return {
desc: interactTypeToString(interactivity.interactType),
response: interactivity.response,
......@@ -73,17 +81,17 @@ export const FixedSlot = (props: {
})
)
);
dispatch(setCardModalCounters(props.state.counters));
dispatch(setCardModalCounters(snapState.counters));
dispatch(setCardModalIsOpen(true));
// 侧边栏展示超量素材信息
if (
props.state.overlay_materials &&
props.state.overlay_materials.length > 0
snapState.overlay_materials &&
snapState.overlay_materials.length > 0
) {
dispatch(
setCardListModalInfo(
props.state.overlay_materials?.map((overlay) => {
snapState.overlay_materials?.map((overlay) => {
return {
meta: overlay,
interactivies: [],
......@@ -96,7 +104,7 @@ export const FixedSlot = (props: {
}
},
planeRef,
[props.state]
[snapState]
);
return (
......@@ -109,8 +117,8 @@ export const FixedSlot = (props: {
rotation={rotation}
enableEdgesRendering
edgesWidth={
props.state.placeInteractivities ||
props.state.idleInteractivities.length > 0
snapState.placeInteractivities ||
snapState.idleInteractivities.length > 0
? edgesWidth
: 0
}
......@@ -119,15 +127,15 @@ export const FixedSlot = (props: {
<standardMaterial
name={`fixedslot-mat-${props.sequence}`}
diffuseTexture={
props.state.occupant
snapState.occupant
? faceDown
? new BABYLON.Texture(`${NeosConfig.assetsPath}/card_back.jpg`)
: new BABYLON.Texture(
`${NeosConfig.cardImgUrl}/${props.state.occupant.id}.jpg`
`${NeosConfig.cardImgUrl}/${snapState.occupant.id}.jpg`
)
: new BABYLON.Texture(`${NeosConfig.assetsPath}/card_slot.png`)
}
alpha={props.state.occupant ? 1 : 0}
alpha={snapState.occupant ? 1 : 0}
></standardMaterial>
</plane>
);
......
......@@ -10,20 +10,26 @@ import {
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
import { matStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
export const Graveyard = () => {
const meGraveyard = useAppSelector(selectMeGraveyard).inner;
const opGraveyard = useAppSelector(selectOpGraveyard).inner;
// const meGraveyard = useAppSelector(selectMeGraveyard).inner;
// const opGraveyard = useAppSelector(selectOpGraveyard).inner;
const meGraveyard = useSnapshot(matStore.graveyards.me);
const opGraveyard = useSnapshot(matStore.graveyards.op);
return (
<>
<SingleSlot
state={meGraveyard}
state={matStore.graveyards.me}
position={graveyardPosition(0, meGraveyard.length)}
rotation={cardSlotRotation(false)}
/>
<SingleSlot
state={opGraveyard}
state={matStore.graveyards.op}
position={graveyardPosition(1, opGraveyard.length)}
rotation={cardSlotRotation(true)}
/>
......
......@@ -4,7 +4,7 @@ import { useHover } from "react-babylonjs";
import { useConfig } from "@/config";
import { useAppSelector, useClick } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
// import { CardState } from "@/reducers/duel/generic";
import { selectMeHands, selectOpHands } from "@/reducers/duel/handsSlice";
import {
setCardModalInteractivies,
......@@ -12,6 +12,8 @@ import {
setCardModalMeta,
} from "@/reducers/duel/mod";
import { store } from "@/store";
import { matStore, type CardState, messageStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
import { animated, useSpring } from "../spring";
import { interactTypeToString, zip } from "../utils";
......@@ -26,9 +28,12 @@ const handRotation = new BABYLON.Vector3(rotation.x, rotation.y, rotation.z);
const hoverScaling = NeosConfig.ui.card.handHoverScaling;
export const Hands = () => {
const meHands = useAppSelector(selectMeHands).inner;
const meHands = useSnapshot(matStore.hands.me);
const opHands = useSnapshot(matStore.hands.op);
// const meHands = useAppSelector(selectMeHands).inner;
// const opHands = useAppSelector(selectOpHands).inner;
const meHandPositions = handPositons(0, meHands);
const opHands = useAppSelector(selectOpHands).inner;
const opHandPositions = handPositons(1, opHands);
return (
......@@ -116,6 +121,7 @@ const CHand = (props: {
() => {
if (state.occupant) {
dispatch(setCardModalMeta(state.occupant));
messageStore.cardModal.meta = state.occupant;
}
dispatch(
setCardModalInteractivies(
......@@ -127,7 +133,14 @@ const CHand = (props: {
})
)
);
messageStore.cardModal.interactivies = state.idleInteractivities.map(
(interactive) => ({
desc: interactTypeToString(interactive.interactType),
response: interactive.response,
})
);
dispatch(setCardModalIsOpen(true));
messageStore.cardModal.isOpen = true;
},
planeRef,
[state]
......@@ -145,7 +158,8 @@ const CHand = (props: {
rotation={props.rotation}
enableEdgesRendering
edgesWidth={
state.idleInteractivities.length > 0 || state.placeInteractivities
// state.idleInteractivities.length > 0 || state.placeInteractivities
state.idleInteractivities.length > 0 || state.placeInteractivity
? edgesWidth
: 0
}
......
......@@ -2,13 +2,16 @@ import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
// import { CardState } from "@/reducers/duel/generic";
import { selectMeMagics, selectOpMagics } from "@/reducers/duel/magicSlice";
import { clearMagicPlaceInteractivities } from "@/reducers/duel/mod";
import { cardSlotRotation, zip } from "../utils";
import { FixedSlot } from "./FixedSlot";
import { matStore, type CardState, messageStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
// TODO: use config
const left = -2.15;
......@@ -16,11 +19,17 @@ const gap = 1.05;
const transform = NeosConfig.ui.card.transform;
export const Magics = () => {
const meMagics = useAppSelector(selectMeMagics).inner;
// const meMagics = useAppSelector(selectMeMagics).inner;
// const opMagics = useAppSelector(selectOpMagics).inner;
const meMagics = useSnapshot(matStore.magics.me);
const opMagics = useSnapshot(matStore.magics.op);
const meMagicPositions = magicPositions(0, meMagics);
const opMagics = useAppSelector(selectOpMagics).inner;
const opMagicPositions = magicPositions(1, opMagics);
const clearPlaceInteractivitiesAction = (controller: number) =>
matStore.magics.of(controller).clearPlaceInteractivity();
return (
<>
{zip(meMagics, meMagicPositions)
......@@ -28,12 +37,13 @@ export const Magics = () => {
.map(([magic, position], sequence) => {
return (
<FixedSlot
state={magic}
snapState={magic}
key={sequence}
sequence={sequence}
position={position}
rotation={cardSlotRotation(false)}
clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
// clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
);
})}
......@@ -42,12 +52,13 @@ export const Magics = () => {
.map(([magic, position], sequence) => {
return (
<FixedSlot
state={magic}
snapState={magic}
key={sequence}
sequence={sequence}
position={position}
rotation={cardSlotRotation(true)}
clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
// clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
);
})}
......
......@@ -4,7 +4,7 @@ import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
// import { CardState } from "@/reducers/duel/generic";
import { clearMonsterPlaceInteractivities } from "@/reducers/duel/mod";
import {
selectMeMonsters,
......@@ -14,16 +14,24 @@ import {
import { cardSlotDefenceRotation, cardSlotRotation, zip } from "../utils";
import { FixedSlot } from "./FixedSlot";
import { matStore, type CardState, messageStore } from "@/valtioStores";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
const transform = NeosConfig.ui.card.transform;
const floating = NeosConfig.ui.card.floating;
const left = -2.15; // TODO: config
const gap = 1.05;
const clearPlaceInteractivitiesAction = (controller: number) =>
matStore.monsters.of(controller).clearPlaceInteractivity();
export const Monsters = () => {
const meMonsters = useAppSelector(selectMeMonsters).inner;
// const meMonsters = useAppSelector(selectMeMonsters).inner;
// const opMonsters = useAppSelector(selectOpMonsters).inner;
const meMonsters = useSnapshot(matStore.monsters.me);
const opMonsters = useSnapshot(matStore.monsters.op);
const meMonsterPositions = monsterPositions(0, meMonsters);
const opMonsters = useAppSelector(selectOpMonsters).inner;
const opMonsterPositions = monsterPositions(1, opMonsters);
return (
......@@ -32,26 +40,28 @@ export const Monsters = () => {
.slice(0, 5)
.map(([monster, position], sequence) => (
<FixedSlot
state={monster}
snapState={monster}
key={sequence}
sequence={sequence}
position={position}
rotation={cardSlotRotation(false)}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
// clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
))}
{zip(opMonsters, opMonsterPositions)
.slice(0, 5)
.map(([monster, position], sequence) => (
<FixedSlot
state={monster}
snapState={monster}
key={sequence}
sequence={sequence}
position={position}
rotation={cardSlotRotation(true)}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
// clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
))}
<ExtraMonsters meMonsters={meMonsters} opMonsters={opMonsters} />
......@@ -79,31 +89,33 @@ const ExtraMonsters = (props: {
<>
{meLeft ? (
<FixedSlot
state={meLeft}
snapState={meLeft}
sequence={5}
position={leftPosition}
rotation={meRotation}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
// clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
) : (
<></>
)}
{meRight ? (
<FixedSlot
state={meRight}
snapState={meRight}
sequence={6}
position={rightPosition}
rotation={meRotation}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
// clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
clearPlaceInteractivitiesAction={clearPlaceInteractivitiesAction}
/>
) : (
<></>
)}
{opLeft ? (
<FixedSlot
state={opLeft}
snapState={opLeft}
sequence={5}
position={rightPosition}
rotation={opRotation}
......@@ -115,7 +127,7 @@ const ExtraMonsters = (props: {
)}
{opRight ? (
<FixedSlot
state={opRight}
snapState={opRight}
sequence={6}
position={leftPosition}
rotation={opRotation}
......
......@@ -11,6 +11,7 @@ import {
import { store } from "@/store";
import { interactTypeToString } from "../utils";
import { useSnapshot } from "valtio";
const NeosConfig = useConfig();
const transform = NeosConfig.ui.card.transform;
......@@ -21,10 +22,11 @@ export const SingleSlot = (props: {
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
}) => {
const snapState = useSnapshot(props.state);
const boxRef = useRef(null);
const dispatch = store.dispatch;
const edgeRender =
props.state.find((item) =>
snapState.find((item) =>
item === undefined ? false : item.idleInteractivities.length > 0
) !== undefined;
const edgesWidth = 2.0;
......@@ -32,10 +34,10 @@ export const SingleSlot = (props: {
useClick(
(_event) => {
if (props.state.length != 0) {
if (snapState.length != 0) {
dispatch(
setCardListModalInfo(
props.state
snapState
.filter(
(item) => item.occupant !== undefined && item.occupant.id !== 0
)
......@@ -56,7 +58,7 @@ export const SingleSlot = (props: {
}
},
boxRef,
[props.state]
[snapState]
);
return (
......@@ -64,11 +66,7 @@ export const SingleSlot = (props: {
name="single-slot"
ref={boxRef}
scaling={
new BABYLON.Vector3(
transform.x,
transform.y,
Depth * props.state.length
)
new BABYLON.Vector3(transform.x, transform.y, Depth * snapState.length)
}
position={props.position}
rotation={props.rotation}
......@@ -81,7 +79,7 @@ export const SingleSlot = (props: {
diffuseTexture={
new BABYLON.Texture(`${NeosConfig.assetsPath}/card_back.jpg`)
}
alpha={props.state.length == 0 ? 0 : 1}
alpha={snapState.length == 0 ? 0 : 1}
/>
</box>
);
......
......@@ -42,6 +42,8 @@ export type DuelFieldState = CardState[] & {
clearPlaceInteractivity: () => void;
};
type test = DuelFieldState extends (infer S)[] ? S : never;
export interface MatState {
selfType: number;
......
import { matStore } from "@/valtioStores";
import { ygopro } from "@/api";
/** 清空所有place互动性,也可以删除某一个zone的互动性。zone为空则为清除所有。 */
export const clearAllPlaceInteradtivities = (
controller: number,
zone?: ygopro.CardZone
) => {
if (zone) {
matStore.in(zone).of(controller).clearPlaceInteractivity();
} else {
matStore.banishedZones.of(controller).clearPlaceInteractivity();
matStore.decks.of(controller).clearPlaceInteractivity();
matStore.extraDecks.of(controller).clearPlaceInteractivity();
matStore.graveyards.of(controller).clearPlaceInteractivity();
matStore.hands.of(controller).clearPlaceInteractivity();
matStore.magics.of(controller).clearPlaceInteractivity();
matStore.monsters.of(controller).clearPlaceInteractivity();
}
};
export * from "./clearAllIdleInteractivities";
export * from "./fetchCheckCardMetasV2";
export * from "./fetchCheckCardMetasV3";
export * from "./clearAllPlaceInteradtivities";
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