Commit 90419ce0 authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/reducers' into 'main'

Feat/reducers

See merge request mycard/Neos!71
parents 9c3989e3 b33802fc
Pipeline #19449 passed with stages
in 4 minutes and 31 seconds
...@@ -17,6 +17,7 @@ export const DeckSlotShape = () => { ...@@ -17,6 +17,7 @@ export const DeckSlotShape = () => {
export const ExtraDeckSlotShape = () => { export const ExtraDeckSlotShape = () => {
return { width: 0.8, height: 1, depth: 0.2 }; return { width: 0.8, height: 1, depth: 0.2 };
}; };
export const SingleSlotShape = { width: 0.8, height: 1, depth: 0.2 };
export const CemeterySlotShape = () => { export const CemeterySlotShape = () => {
return { width: 0.8, height: 1, depth: 0.2 }; return { width: 0.8, height: 1, depth: 0.2 };
}; };
......
...@@ -2,18 +2,19 @@ import { judgeSelf } from "./util"; ...@@ -2,18 +2,19 @@ import { judgeSelf } from "./util";
import { import {
PayloadAction, PayloadAction,
CaseReducer, CaseReducer,
createAsyncThunk,
ActionReducerMapBuilder, ActionReducerMapBuilder,
} from "@reduxjs/toolkit"; } from "@reduxjs/toolkit";
import { DuelState } from "./mod"; import { DuelState } from "./mod";
import { RootState } from "../../store"; import { RootState } from "../../store";
import { fetchCard } from "../../api/cards";
import { CardState } from "./generic";
import { ygopro } from "../../api/ocgcore/idl/ocgcore"; import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import {
createAsyncMetaThunk,
DuelFieldState,
extendState,
extendMeta,
} from "./generic";
export interface CemeteryState { export interface CemeteryState extends DuelFieldState {}
cemetery: CardState[];
}
// 初始化墓地状态 // 初始化墓地状态
export const initCemeteryImpl: CaseReducer<DuelState, PayloadAction<number>> = ( export const initCemeteryImpl: CaseReducer<DuelState, PayloadAction<number>> = (
...@@ -22,28 +23,14 @@ export const initCemeteryImpl: CaseReducer<DuelState, PayloadAction<number>> = ( ...@@ -22,28 +23,14 @@ export const initCemeteryImpl: CaseReducer<DuelState, PayloadAction<number>> = (
) => { ) => {
const player = action.payload; const player = action.payload;
if (judgeSelf(player, state)) { if (judgeSelf(player, state)) {
state.meCemetery = { cemetery: [] }; state.meCemetery = { inner: [] };
} else { } else {
state.opCemetery = { cemetery: [] }; state.opCemetery = { inner: [] };
} }
}; };
// 增加墓地 // 增加墓地
export const fetchCemeteryMeta = createAsyncThunk( export const fetchCemeteryMeta = createAsyncMetaThunk("duel/fetchCemeteryMeta");
"duel/fetchCemeteryMeta",
async (param: { controler: number; sequence: number; code: number }) => {
const code = param.code;
const meta = await fetchCard(code);
const response = {
controler: param.controler,
sequence: param.sequence,
meta,
};
return response;
}
);
export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => { export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchCemeteryMeta.pending, (state, action) => { builder.addCase(fetchCemeteryMeta.pending, (state, action) => {
...@@ -62,17 +49,9 @@ export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -62,17 +49,9 @@ export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => {
idleInteractivities: [], idleInteractivities: [],
}; };
if (judgeSelf(controler, state)) { if (judgeSelf(controler, state)) {
if (state.meCemetery) { extendState(state.meCemetery, newCemetery);
state.meCemetery.cemetery.push(newCemetery);
} else {
state.meCemetery = { cemetery: [newCemetery] };
}
} else { } else {
if (state.opCemetery) { extendState(state.opCemetery, newCemetery);
state.opCemetery.cemetery.push(newCemetery);
} else {
state.opCemetery = { cemetery: [newCemetery] };
}
} }
}); });
builder.addCase(fetchCemeteryMeta.fulfilled, (state, action) => { builder.addCase(fetchCemeteryMeta.fulfilled, (state, action) => {
...@@ -81,26 +60,14 @@ export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -81,26 +60,14 @@ export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const meta = action.payload.meta; const meta = action.payload.meta;
if (judgeSelf(controler, state)) { if (judgeSelf(controler, state)) {
if (state.meCemetery) { extendMeta(state.meCemetery, meta, sequence);
for (const cemetery of state.meCemetery.cemetery) {
if (cemetery.location.sequence == sequence) {
cemetery.occupant = meta;
}
}
}
} else { } else {
if (state.opCemetery) { extendMeta(state.opCemetery, meta, sequence);
for (const cemetery of state.opCemetery.cemetery) {
if (cemetery.location.sequence == sequence) {
cemetery.occupant = meta;
}
}
}
} }
}); });
}; };
export const selectMeCemetery = (state: RootState) => export const selectMeCemetery = (state: RootState) =>
state.duel.meCemetery || { cemetery: [] }; state.duel.meCemetery || { inner: [] };
export const selectOpCemetery = (state: RootState) => export const selectOpCemetery = (state: RootState) =>
state.duel.opCemetery || { cemetery: [] }; state.duel.opCemetery || { inner: [] };
import { judgeSelf } from "./util";
import {
PayloadAction,
CaseReducer,
ActionReducerMapBuilder,
} from "@reduxjs/toolkit";
import { DuelState } from "./mod";
import { RootState } from "../../store";
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import {
createAsyncMetaThunk,
DuelFieldState,
extendState,
extendMeta,
} from "./generic";
export interface ExclusionState extends DuelFieldState {}
// 初始化除外区状态
export const initExclusionImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
const player = action.payload;
if (judgeSelf(player, state)) {
state.meExclusion = { inner: [] };
} else {
state.opExclusion = { inner: [] };
}
};
// 增加除外区
export const fetchExclusionMeta = createAsyncMetaThunk(
"duel/fetchExclusionMeta"
);
export const exclusionCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchExclusionMeta.pending, (state, action) => {
// Meta结果没返回之前先更新`ID`
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
const code = action.meta.arg.code;
const newExclusion = {
occupant: { id: code, data: {}, text: {} },
location: {
controler,
location: ygopro.CardZone.REMOVED,
sequence,
},
idleInteractivities: [],
};
if (judgeSelf(controler, state)) {
extendState(state.meExclusion, newExclusion);
} else {
extendState(state.opExclusion, newExclusion);
}
});
builder.addCase(fetchExclusionMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const meta = action.payload.meta;
if (judgeSelf(controler, state)) {
extendMeta(state.meExclusion, meta, sequence);
} else {
extendMeta(state.opExclusion, meta, sequence);
}
});
};
export const selectMeExclusion = (state: RootState) =>
state.duel.meExclusion || { inner: [] };
export const selectopExclusion = (state: RootState) =>
state.duel.opExclusion || { inner: [] };
import { AsyncThunk, createAsyncThunk } from "@reduxjs/toolkit";
import { CardMeta } from "../../api/cards"; import { CardMeta } from "../../api/cards";
import { ygopro } from "../../api/ocgcore/idl/ocgcore"; import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { fetchCard } from "../../api/cards";
export interface DuelFieldState {
inner: CardState[];
}
export interface CardState { export interface CardState {
occupant?: CardMeta; // 占据此位置的卡牌元信息 occupant?: CardMeta; // 占据此位置的卡牌元信息
...@@ -42,3 +48,108 @@ export interface Interactivity<T> { ...@@ -42,3 +48,108 @@ export interface Interactivity<T> {
// 用户点击后,需要回传给服务端的`response` // 用户点击后,需要回传给服务端的`response`
response: T; response: T;
} }
export function createAsyncMetaThunk(name: string): AsyncThunk<
{ controler: number; sequence: number; meta: CardMeta },
{
controler: number;
sequence: number;
position?: ygopro.CardPosition;
code: number;
},
{}
> {
return createAsyncThunk(
name,
async (param: {
controler: number;
sequence: number;
position?: ygopro.CardPosition;
code: number;
}) => {
const code = param.code;
const meta = await fetchCard(code);
const response = {
controler: param.controler,
sequence: param.sequence,
meta,
};
return response;
}
);
}
export function extendState<T extends DuelFieldState>(
state: T | undefined,
newState: CardState
) {
if (state) {
state.inner.push(newState);
}
}
export function extendOccupant<T extends DuelFieldState>(
state: T | undefined,
newMeta: CardMeta,
sequence: number,
position?: ygopro.CardPosition
) {
if (state) {
for (const item of state.inner) {
if (item.location.sequence == sequence) {
item.occupant = newMeta;
if (position) {
item.location.position = position;
}
}
}
}
}
export function extendMeta<T extends DuelFieldState>(
state: T | undefined,
newMeta: CardMeta,
sequence: number
) {
if (state) {
for (const item of state.inner) {
if (item.location.sequence == sequence) {
item.occupant = newMeta;
}
}
}
}
export function extendPlaceInteractivity<T extends DuelFieldState>(
state: T | undefined,
controler: number,
sequence: number,
zone: ygopro.CardZone
) {
if (state) {
for (let item of state.inner) {
if (item.location.sequence == sequence) {
item.placeInteractivities = {
interactType: InteractType.PLACE_SELECTABLE,
response: {
controler,
zone,
sequence,
},
};
}
}
}
}
export function clearPlaceInteractivities<T extends DuelFieldState>(
state: T | undefined
) {
if (state) {
for (let item of state.inner) {
item.placeInteractivities = undefined;
}
}
}
...@@ -8,13 +8,10 @@ import { DuelState } from "./mod"; ...@@ -8,13 +8,10 @@ import { DuelState } from "./mod";
import { RootState } from "../../store"; import { RootState } from "../../store";
import { fetchCard, CardMeta } from "../../api/cards"; import { fetchCard, CardMeta } from "../../api/cards";
import { judgeSelf } from "./util"; import { judgeSelf } from "./util";
import { CardState, Interactivity } from "./generic"; import { Interactivity, DuelFieldState } from "./generic";
import { ygopro } from "../../api/ocgcore/idl/ocgcore"; import { ygopro } from "../../api/ocgcore/idl/ocgcore";
export interface HandState { export interface HandState extends DuelFieldState {}
// 注意:手牌的位置顺序是有约束的
hands: CardState[];
}
// 增加手牌 // 增加手牌
export const fetchHandsMeta = createAsyncThunk( export const fetchHandsMeta = createAsyncThunk(
...@@ -57,15 +54,15 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -57,15 +54,15 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
}); });
if (judgeSelf(player, state)) { if (judgeSelf(player, state)) {
if (state.meHands) { if (state.meHands) {
state.meHands.hands = state.meHands.hands.concat(cards); state.meHands.inner = state.meHands.inner.concat(cards);
} else { } else {
state.meHands = { hands: cards }; state.meHands = { inner: cards };
} }
} else { } else {
if (state.opHands) { if (state.opHands) {
state.opHands.hands = state.opHands.hands.concat(cards); state.opHands.inner = state.opHands.inner.concat(cards);
} else { } else {
state.opHands = { hands: cards }; state.opHands = { inner: cards };
} }
} }
}); });
...@@ -76,7 +73,7 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -76,7 +73,7 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const hands = judgeSelf(player, state) ? state.meHands : state.opHands; const hands = judgeSelf(player, state) ? state.meHands : state.opHands;
if (hands) { if (hands) {
for (let hand of hands.hands) { for (let hand of hands.inner) {
for (let meta of metas) { for (let meta of metas) {
if (hand.occupant?.id === meta.id) { if (hand.occupant?.id === meta.id) {
hand.occupant = meta; hand.occupant = meta;
...@@ -97,7 +94,7 @@ export const clearHandsIdleInteractivityImpl: CaseReducer< ...@@ -97,7 +94,7 @@ export const clearHandsIdleInteractivityImpl: CaseReducer<
const hands = judgeSelf(player, state) ? state.meHands : state.opHands; const hands = judgeSelf(player, state) ? state.meHands : state.opHands;
if (hands) { if (hands) {
for (let hand of hands.hands) { for (let hand of hands.inner) {
hand.idleInteractivities = []; hand.idleInteractivities = [];
} }
} }
...@@ -119,7 +116,7 @@ export const addHandsIdleInteractivityImpl: CaseReducer< ...@@ -119,7 +116,7 @@ export const addHandsIdleInteractivityImpl: CaseReducer<
const sequence = action.payload.sequence; const sequence = action.payload.sequence;
const interactivity = action.payload.interactivity; const interactivity = action.payload.interactivity;
hands.hands[sequence].idleInteractivities.push(interactivity); hands.inner[sequence].idleInteractivities.push(interactivity);
} }
}; };
...@@ -133,13 +130,13 @@ export const removeHandImpl: CaseReducer< ...@@ -133,13 +130,13 @@ export const removeHandImpl: CaseReducer<
const hands = judgeSelf(controler, state) ? state.meHands : state.opHands; const hands = judgeSelf(controler, state) ? state.meHands : state.opHands;
if (hands) { if (hands) {
hands.hands = hands.hands.filter( hands.inner = hands.inner.filter(
(card) => card.location.sequence != sequence (card) => card.location.sequence != sequence
); );
} }
}; };
export const selectMeHands = (state: RootState) => export const selectMeHands = (state: RootState) =>
state.duel.meHands || { hands: [] }; state.duel.meHands || { inner: [] };
export const selectOpHands = (state: RootState) => export const selectOpHands = (state: RootState) =>
state.duel.opHands || { hands: [] }; state.duel.opHands || { inner: [] };
...@@ -2,18 +2,20 @@ import { judgeSelf } from "./util"; ...@@ -2,18 +2,20 @@ import { judgeSelf } from "./util";
import { import {
PayloadAction, PayloadAction,
CaseReducer, CaseReducer,
createAsyncThunk,
ActionReducerMapBuilder, ActionReducerMapBuilder,
} from "@reduxjs/toolkit"; } from "@reduxjs/toolkit";
import { DuelState } from "./mod"; import { DuelState } from "./mod";
import { ygopro } from "../../api/ocgcore/idl/ocgcore"; import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { RootState } from "../../store"; import { RootState } from "../../store";
import { fetchCard } from "../../api/cards"; import {
import { CardState, InteractType } from "./generic"; createAsyncMetaThunk,
DuelFieldState,
extendOccupant,
extendPlaceInteractivity,
clearPlaceInteractivities,
} from "./generic";
export interface MagicState { export interface MagicState extends DuelFieldState {}
magics: CardState[];
}
// 初始化自己的魔法陷阱区状态 // 初始化自己的魔法陷阱区状态
export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = ( export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = (
...@@ -22,7 +24,7 @@ export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = ( ...@@ -22,7 +24,7 @@ export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = (
) => { ) => {
const player = action.payload; const player = action.payload;
const magics = { const magics = {
magics: [ inner: [
{ {
location: { location: {
controler: player, controler: player,
...@@ -81,20 +83,7 @@ export const addMagicPlaceInteractivitiesImpl: CaseReducer< ...@@ -81,20 +83,7 @@ export const addMagicPlaceInteractivitiesImpl: CaseReducer<
const sequence = action.payload[1]; const sequence = action.payload[1];
const magics = judgeSelf(controler, state) ? state.meMagics : state.opMagics; const magics = judgeSelf(controler, state) ? state.meMagics : state.opMagics;
if (magics) { extendPlaceInteractivity(magics, controler, sequence, ygopro.CardZone.SZONE);
for (const magic of magics.magics) {
if (magic.location.sequence == sequence) {
magic.placeInteractivities = {
interactType: InteractType.PLACE_SELECTABLE,
response: {
controler,
zone: ygopro.CardZone.SZONE,
sequence,
},
};
}
}
}
}; };
export const clearMagicPlaceInteractivitiesImpl: CaseReducer< export const clearMagicPlaceInteractivitiesImpl: CaseReducer<
...@@ -104,35 +93,11 @@ export const clearMagicPlaceInteractivitiesImpl: CaseReducer< ...@@ -104,35 +93,11 @@ export const clearMagicPlaceInteractivitiesImpl: CaseReducer<
const player = action.payload; const player = action.payload;
const magics = judgeSelf(player, state) ? state.meMagics : state.opMagics; const magics = judgeSelf(player, state) ? state.meMagics : state.opMagics;
clearPlaceInteractivities(magics);
if (magics) {
for (const magic of magics.magics) {
magic.placeInteractivities = undefined;
}
}
}; };
// 增加魔法陷阱 // 增加魔法陷阱
export const fetchMagicMeta = createAsyncThunk( export const fetchMagicMeta = createAsyncMetaThunk("duel/fetchMagicMeta");
"duel/fetchMagicMeta",
async (param: {
controler: number;
sequence: number;
position: ygopro.CardPosition;
code: number;
}) => {
const code = param.code;
const meta = await fetchCard(code);
const response = {
controler: param.controler,
sequence: param.sequence,
meta,
};
return response;
}
);
export const magicCase = (builder: ActionReducerMapBuilder<DuelState>) => { export const magicCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchMagicMeta.pending, (state, action) => { builder.addCase(fetchMagicMeta.pending, (state, action) => {
...@@ -144,23 +109,9 @@ export const magicCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -144,23 +109,9 @@ export const magicCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const meta = { id: code, data: {}, text: {} }; const meta = { id: code, data: {}, text: {} };
if (judgeSelf(controler, state)) { if (judgeSelf(controler, state)) {
if (state.meMagics) { extendOccupant(state.meMagics, meta, sequence, position);
for (const magic of state.meMagics.magics) {
if (magic.location.sequence == sequence) {
magic.occupant = meta;
magic.location.position = position;
}
}
}
} else { } else {
if (state.opMagics) { extendOccupant(state.opMagics, meta, sequence, position);
for (const magic of state.opMagics.magics) {
if (magic.location.sequence == sequence) {
magic.occupant = meta;
magic.location.position = position;
}
}
}
} }
}); });
builder.addCase(fetchMagicMeta.fulfilled, (state, action) => { builder.addCase(fetchMagicMeta.fulfilled, (state, action) => {
...@@ -169,26 +120,14 @@ export const magicCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -169,26 +120,14 @@ export const magicCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const meta = action.payload.meta; const meta = action.payload.meta;
if (judgeSelf(controler, state)) { if (judgeSelf(controler, state)) {
if (state.meMagics) { extendOccupant(state.meMagics, meta, sequence);
for (const magic of state.meMagics.magics) {
if (magic.location.sequence == sequence) {
magic.occupant = meta;
}
}
}
} else { } else {
if (state.opMagics) { extendOccupant(state.opMagics, meta, sequence);
for (const magic of state.opMagics.magics) {
if (magic.location.sequence == sequence) {
magic.occupant = meta;
}
}
}
} }
}); });
}; };
export const selectMeMagics = (state: RootState) => export const selectMeMagics = (state: RootState) =>
state.duel.meMagics || { magics: [] }; state.duel.meMagics || { inner: [] };
export const selectOpMagics = (state: RootState) => export const selectOpMagics = (state: RootState) =>
state.duel.opMagics || { magics: [] }; state.duel.opMagics || { inner: [] };
...@@ -56,6 +56,11 @@ import { ...@@ -56,6 +56,11 @@ import {
magicCase, magicCase,
} from "./magicSlice"; } from "./magicSlice";
import { CemeteryState, initCemeteryImpl, cemeteryCase } from "./cemeretySlice"; import { CemeteryState, initCemeteryImpl, cemeteryCase } from "./cemeretySlice";
import {
ExclusionState,
initExclusionImpl,
exclusionCase,
} from "./exclusionSlice";
export interface DuelState { export interface DuelState {
selfType?: number; selfType?: number;
...@@ -74,6 +79,9 @@ export interface DuelState { ...@@ -74,6 +79,9 @@ export interface DuelState {
meCemetery?: CemeteryState; // 自己的墓地状态 meCemetery?: CemeteryState; // 自己的墓地状态
opCemetery?: CemeteryState; // 对手的墓地状态 opCemetery?: CemeteryState; // 对手的墓地状态
meExclusion?: ExclusionState; // 自己的除外区状态
opExclusion?: ExclusionState; // 对手的除外区状态
meTimeLimit?: TimeLimit; // 自己的计时 meTimeLimit?: TimeLimit; // 自己的计时
opTimeLimit?: TimeLimit; // 对手的计时 opTimeLimit?: TimeLimit; // 对手的计时
...@@ -128,6 +136,9 @@ const duelSlice = createSlice({ ...@@ -128,6 +136,9 @@ const duelSlice = createSlice({
// 墓地相关`Reducer` // 墓地相关`Reducer`
initCemetery: initCemeteryImpl, initCemetery: initCemeteryImpl,
// 除外区相关`Reducer`
initExclusion: initExclusionImpl,
// UI相关`Reducer` // UI相关`Reducer`
setCardModalIsOpen: setCardModalIsOpenImpl, setCardModalIsOpen: setCardModalIsOpenImpl,
setCardModalText: setCardModalTextImpl, setCardModalText: setCardModalTextImpl,
...@@ -154,6 +165,7 @@ const duelSlice = createSlice({ ...@@ -154,6 +165,7 @@ const duelSlice = createSlice({
monsterCase(builder); monsterCase(builder);
magicCase(builder); magicCase(builder);
cemeteryCase(builder); cemeteryCase(builder);
exclusionCase(builder);
checkCardModalCase(builder); checkCardModalCase(builder);
YesNoModalCase(builder); YesNoModalCase(builder);
optionModalCase(builder); optionModalCase(builder);
......
...@@ -2,18 +2,20 @@ import { judgeSelf } from "./util"; ...@@ -2,18 +2,20 @@ import { judgeSelf } from "./util";
import { import {
PayloadAction, PayloadAction,
CaseReducer, CaseReducer,
createAsyncThunk,
ActionReducerMapBuilder, ActionReducerMapBuilder,
} from "@reduxjs/toolkit"; } from "@reduxjs/toolkit";
import { DuelState } from "./mod"; import { DuelState } from "./mod";
import { ygopro } from "../../api/ocgcore/idl/ocgcore"; import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { RootState } from "../../store"; import { RootState } from "../../store";
import { fetchCard } from "../../api/cards"; import {
import { CardState, InteractType } from "./generic"; DuelFieldState,
createAsyncMetaThunk,
extendOccupant,
extendPlaceInteractivity,
clearPlaceInteractivities,
} from "./generic";
export interface MonsterState { export interface MonsterState extends DuelFieldState {}
monsters: CardState[];
}
// 初始化怪兽区状态 // 初始化怪兽区状态
export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = ( export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
...@@ -22,7 +24,7 @@ export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = ( ...@@ -22,7 +24,7 @@ export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
) => { ) => {
const player = action.payload; const player = action.payload;
const monsters = { const monsters = {
monsters: [ inner: [
{ {
location: { location: {
controler: player, controler: player,
...@@ -83,20 +85,12 @@ export const addMonsterPlaceInteractivitiesImpl: CaseReducer< ...@@ -83,20 +85,12 @@ export const addMonsterPlaceInteractivitiesImpl: CaseReducer<
const monsters = judgeSelf(controler, state) const monsters = judgeSelf(controler, state)
? state.meMonsters ? state.meMonsters
: state.opMonsters; : state.opMonsters;
if (monsters) { extendPlaceInteractivity(
for (const monster of monsters.monsters) { monsters,
if (monster.location.sequence == sequence) { controler,
monster.placeInteractivities = { sequence,
interactType: InteractType.PLACE_SELECTABLE, ygopro.CardZone.MZONE
response: { );
controler,
zone: ygopro.CardZone.MZONE,
sequence,
},
};
}
}
}
}; };
export const clearMonsterPlaceInteractivitiesImpl: CaseReducer< export const clearMonsterPlaceInteractivitiesImpl: CaseReducer<
...@@ -109,34 +103,11 @@ export const clearMonsterPlaceInteractivitiesImpl: CaseReducer< ...@@ -109,34 +103,11 @@ export const clearMonsterPlaceInteractivitiesImpl: CaseReducer<
? state.meMonsters ? state.meMonsters
: state.opMonsters; : state.opMonsters;
if (monsters) { clearPlaceInteractivities(monsters);
for (const monster of monsters.monsters) {
monster.placeInteractivities = undefined;
}
}
}; };
// 增加怪兽 // 增加怪兽
export const fetchMonsterMeta = createAsyncThunk( export const fetchMonsterMeta = createAsyncMetaThunk("duel/fetchMonsterMeta");
"duel/fetchMonsterMeta",
async (param: {
controler: number;
sequence: number;
position: ygopro.CardPosition;
code: number;
}) => {
const code = param.code;
const meta = await fetchCard(code);
const response = {
controler: param.controler,
sequence: param.sequence,
meta,
};
return response;
}
);
export const monsterCase = (builder: ActionReducerMapBuilder<DuelState>) => { export const monsterCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchMonsterMeta.pending, (state, action) => { builder.addCase(fetchMonsterMeta.pending, (state, action) => {
...@@ -148,23 +119,9 @@ export const monsterCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -148,23 +119,9 @@ export const monsterCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const meta = { id: code, data: {}, text: {} }; const meta = { id: code, data: {}, text: {} };
if (judgeSelf(controler, state)) { if (judgeSelf(controler, state)) {
if (state.meMonsters) { extendOccupant(state.meMonsters, meta, sequence, position);
for (const monster of state.meMonsters.monsters) {
if (monster.location.sequence == sequence) {
monster.occupant = meta;
monster.location.position = position;
}
}
}
} else { } else {
if (state.opMonsters) { extendOccupant(state.opMonsters, meta, sequence, position);
for (const monster of state.opMonsters.monsters) {
if (monster.location.sequence == sequence) {
monster.occupant = meta;
monster.location.position = position;
}
}
}
} }
}); });
builder.addCase(fetchMonsterMeta.fulfilled, (state, action) => { builder.addCase(fetchMonsterMeta.fulfilled, (state, action) => {
...@@ -173,26 +130,14 @@ export const monsterCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -173,26 +130,14 @@ export const monsterCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const meta = action.payload.meta; const meta = action.payload.meta;
if (judgeSelf(controler, state)) { if (judgeSelf(controler, state)) {
if (state.meMonsters) { extendOccupant(state.meMonsters, meta, sequence);
for (const monster of state.meMonsters.monsters) {
if (monster.location.sequence == sequence) {
monster.occupant = meta;
}
}
}
} else { } else {
if (state.opMonsters) { extendOccupant(state.opMonsters, meta, sequence);
for (const monster of state.opMonsters.monsters) {
if (monster.location.sequence == sequence) {
monster.occupant = meta;
}
}
}
} }
}); });
}; };
export const selectMeMonsters = (state: RootState) => export const selectMeMonsters = (state: RootState) =>
state.duel.meMonsters || { monsters: [] }; state.duel.meMonsters || { inner: [] };
export const selectOpMonsters = (state: RootState) => export const selectOpMonsters = (state: RootState) =>
state.duel.opMonsters || { monsters: [] }; state.duel.opMonsters || { inner: [] };
import * as BABYLON from "@babylonjs/core"; import * as BABYLON from "@babylonjs/core";
import * as CONFIG from "../../config/ui"; import * as CONFIG from "../../config/ui";
import { CardState } from "../../reducers/duel/generic";
import { import {
selectMeCemetery, selectMeCemetery,
selectOpCemetery, selectOpCemetery,
} from "../../reducers/duel/cemeretySlice"; } from "../../reducers/duel/cemeretySlice";
import { store } from "../../store";
import { useAppSelector } from "../../hook"; import { useAppSelector } from "../../hook";
import { useClick } from "./hook"; import SingleSlot from "./singleSlot";
import { useRef } from "react";
import {
setCardListModalInfo,
setCardListModalIsOpen,
} from "../../reducers/duel/mod";
const shape = CONFIG.CemeterySlotShape();
const depth = 0.02; const depth = 0.02;
const Cemeteries = () => { const Cemeteries = () => {
const meCemetery = useAppSelector(selectMeCemetery).cemetery; const meCemetery = useAppSelector(selectMeCemetery).inner;
const opCemetery = useAppSelector(selectOpCemetery).cemetery; const opCemetery = useAppSelector(selectOpCemetery).inner;
return ( return (
<> <>
<CCemetery <SingleSlot
state={meCemetery} state={meCemetery}
position={cemeteryPosition(0, meCemetery.length)} position={cemeteryPosition(0, meCemetery.length)}
rotation={CONFIG.CardSlotRotation(false)} rotation={CONFIG.CardSlotRotation(false)}
/> />
<CCemetery <SingleSlot
state={opCemetery} state={opCemetery}
position={cemeteryPosition(1, opCemetery.length)} position={cemeteryPosition(1, opCemetery.length)}
rotation={CONFIG.CardSlotRotation(true)} rotation={CONFIG.CardSlotRotation(true)}
...@@ -37,60 +29,6 @@ const Cemeteries = () => { ...@@ -37,60 +29,6 @@ const Cemeteries = () => {
); );
}; };
const CCemetery = (props: {
state: CardState[];
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
}) => {
const boxRef = useRef(null);
const dispatch = store.dispatch;
useClick(
(_event) => {
if (props.state.length != 0) {
dispatch(
setCardListModalInfo(
props.state.map((cemetery) => {
return {
name: cemetery.occupant?.text.name,
desc: cemetery.occupant?.text.desc,
imgUrl: `https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/${cemetery.occupant?.id}.jpg`,
};
})
)
);
dispatch(setCardListModalIsOpen(true));
}
},
boxRef,
[props.state]
);
return (
<box
name="cemetery"
ref={boxRef}
scaling={
new BABYLON.Vector3(
shape.width,
shape.height,
depth * props.state.length
)
}
position={props.position}
rotation={props.rotation}
>
<standardMaterial
name="cemetery-mat"
diffuseTexture={
new BABYLON.Texture(`http://localhost:3030/images/card_back.jpg`)
}
alpha={props.state.length == 0 ? 0 : 1}
/>
</box>
);
};
const cemeteryPosition = (player: number, cemeteryLength: number) => { const cemeteryPosition = (player: number, cemeteryLength: number) => {
const x = player == 0 ? 3.2 : -3.2; const x = player == 0 ? 3.2 : -3.2;
const y = (depth * cemeteryLength) / 2 + CONFIG.Floating; const y = (depth * cemeteryLength) / 2 + CONFIG.Floating;
......
import * as BABYLON from "@babylonjs/core"; import * as BABYLON from "@babylonjs/core";
import * as CONFIG from "../../config/ui"; import * as CONFIG from "../../config/ui";
import { useAppSelector } from "../../hook";
import {
selectMeExclusion,
selectopExclusion,
} from "../../reducers/duel/exclusionSlice";
import SingleSlot from "./singleSlot";
const depth = 0.02;
const Exclusion = () => { const Exclusion = () => {
const shape = CONFIG.ExclusionSlotShape(); const meExclusion = useAppSelector(selectMeExclusion).inner;
const position = new BABYLON.Vector3(3.2, CONFIG.Floating, -0.7); const opExclusion = useAppSelector(selectopExclusion).inner;
const rotation = CONFIG.ExclusionSlotRotation();
return ( return (
<box <>
name="exclusion" <SingleSlot
width={shape.width} state={meExclusion}
height={shape.height} position={exclusionPosition(0, meExclusion.length)}
depth={shape.depth} rotation={CONFIG.CardSlotRotation(false)}
position={position} />
rotation={rotation} <SingleSlot
> state={opExclusion}
<standardMaterial position={exclusionPosition(1, opExclusion.length)}
name="exclusion-mat" rotation={CONFIG.CardSlotRotation(true)}
diffuseColor={CONFIG.ExclusionColor()} />
></standardMaterial> </>
</box>
); );
}; };
const exclusionPosition = (player: number, exclusionLength: number) => {
const x = player == 0 ? 3.2 : -3.2;
const y = (depth * exclusionLength) / 2 + CONFIG.Floating;
const z = player == 0 ? -0.7 : 0.7;
return new BABYLON.Vector3(x, y, z);
};
export default Exclusion; export default Exclusion;
...@@ -22,9 +22,9 @@ const handShape = CONFIG.HandShape(); ...@@ -22,9 +22,9 @@ const handShape = CONFIG.HandShape();
const handRotation = CONFIG.HandRotation(); const handRotation = CONFIG.HandRotation();
const Hands = () => { const Hands = () => {
const meHands = useAppSelector(selectMeHands).hands; const meHands = useAppSelector(selectMeHands).inner;
const meHandPositions = handPositons(0, meHands); const meHandPositions = handPositons(0, meHands);
const opHands = useAppSelector(selectOpHands).hands; const opHands = useAppSelector(selectOpHands).inner;
const opHandPositions = handPositons(1, opHands); const opHandPositions = handPositons(1, opHands);
return ( return (
......
...@@ -22,9 +22,9 @@ const gap = 1.05; ...@@ -22,9 +22,9 @@ const gap = 1.05;
const shape = CONFIG.CardSlotShape(); const shape = CONFIG.CardSlotShape();
const Magics = () => { const Magics = () => {
const meMagics = useAppSelector(selectMeMagics).magics; const meMagics = useAppSelector(selectMeMagics).inner;
const meMagicPositions = magicPositions(0, meMagics); const meMagicPositions = magicPositions(0, meMagics);
const opMagics = useAppSelector(selectOpMagics).magics; const opMagics = useAppSelector(selectOpMagics).inner;
const opMagicPositions = magicPositions(1, opMagics); const opMagicPositions = magicPositions(1, opMagics);
return ( return (
......
...@@ -26,9 +26,9 @@ const left = -2.15; // TODO: config ...@@ -26,9 +26,9 @@ const left = -2.15; // TODO: config
const gap = 1.05; const gap = 1.05;
const Monsters = () => { const Monsters = () => {
const meMonsters = useAppSelector(selectMeMonsters).monsters; const meMonsters = useAppSelector(selectMeMonsters).inner;
const meMonsterPositions = monsterPositions(0, meMonsters); const meMonsterPositions = monsterPositions(0, meMonsters);
const opMonsters = useAppSelector(selectOpMonsters).monsters; const opMonsters = useAppSelector(selectOpMonsters).inner;
const opMonsterPositions = monsterPositions(1, opMonsters); const opMonsterPositions = monsterPositions(1, opMonsters);
return ( return (
......
import * as BABYLON from "@babylonjs/core";
import * as CONFIG from "../../config/ui";
import { CardState } from "../../reducers/duel/generic";
import { store } from "../../store";
import { useClick } from "./hook";
import { useRef } from "react";
import {
setCardListModalInfo,
setCardListModalIsOpen,
} from "../../reducers/duel/mod";
const shape = CONFIG.SingleSlotShape;
const depth = 0.02;
const SingleSlot = (props: {
state: CardState[];
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
}) => {
const boxRef = useRef(null);
const dispatch = store.dispatch;
useClick(
(_event) => {
if (props.state.length != 0) {
dispatch(
setCardListModalInfo(
props.state.map((item) => {
return {
name: item.occupant?.text.name,
desc: item.occupant?.text.desc,
imgUrl: `https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/${item.occupant?.id}.jpg`,
};
})
)
);
dispatch(setCardListModalIsOpen(true));
}
},
boxRef,
[props.state]
);
return (
<box
name="single-slot"
ref={boxRef}
scaling={
new BABYLON.Vector3(
shape.width,
shape.height,
depth * props.state.length
)
}
position={props.position}
rotation={props.rotation}
>
<standardMaterial
name="single-slot-mat"
diffuseTexture={
new BABYLON.Texture(`http://localhost:3030/images/card_back.jpg`)
}
alpha={props.state.length == 0 ? 0 : 1}
/>
</box>
);
};
export default SingleSlot;
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