Commit 3404295a authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/extra_deck' into 'main'

Feat/extra deck

See merge request mycard/Neos!92
parents 7ac66454 a20ba050
Pipeline #19775 passed with stages
in 5 minutes and 15 seconds
...@@ -18,6 +18,7 @@ export const clearAllIdleInteractivitiesImpl: DuelReducer<number> = ( ...@@ -18,6 +18,7 @@ export const clearAllIdleInteractivitiesImpl: DuelReducer<number> = (
state.meMagics, state.meMagics,
state.meCemetery, state.meCemetery,
state.meExclusion, state.meExclusion,
state.meExtraDeck,
] ]
: [ : [
state.opHands, state.opHands,
...@@ -25,6 +26,7 @@ export const clearAllIdleInteractivitiesImpl: DuelReducer<number> = ( ...@@ -25,6 +26,7 @@ export const clearAllIdleInteractivitiesImpl: DuelReducer<number> = (
state.opMagics, state.opMagics,
state.opCemetery, state.opCemetery,
state.opExclusion, state.opExclusion,
state.opExtraDeck,
]; ];
states.forEach((item) => clearIdleInteractivities(item)); states.forEach((item) => clearIdleInteractivities(item));
......
import { ActionReducerMapBuilder } from "@reduxjs/toolkit";
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { RootState } from "../../store";
import {
createAsyncMetaThunk,
createAsyncRepeatedMetaThunk,
DuelFieldState,
DuelReducer,
extendIdleInteractivities,
extendMeta,
extendState,
Interactivity,
removeCard,
updateCardMeta,
} from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface ExtraDeckState extends DuelFieldState {}
// 初始化额外卡组
export const initMeExtraDeckMeta = createAsyncRepeatedMetaThunk(
"duel/initExtraDeckMeta"
);
// 增加额外卡组
export const fetchExtraDeckMeta = createAsyncMetaThunk(
"duel/fetchExtraDeckMeta"
);
export const extraDeckCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(initMeExtraDeckMeta.pending, (state, action) => {
const _ = action.meta.arg.controler;
const ids = action.meta.arg.codes;
const cards = ids.map((id) => {
return {
occupant: { id, data: {}, text: {} },
location: {
location: ygopro.CardZone.EXTRA,
},
idleInteractivities: [],
};
});
state.meExtraDeck = { inner: cards };
});
builder.addCase(initMeExtraDeckMeta.fulfilled, (state, action) => {
const _ = action.payload.controler;
const metas = action.payload.metas;
updateCardMeta(state.meExtraDeck, metas);
});
builder.addCase(fetchExtraDeckMeta.pending, (state, action) => {
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
const code = action.meta.arg.code;
const newExtraDeck = {
occupant: { id: code, data: {}, text: {} },
location: {
controler,
location: ygopro.CardZone.EXTRA,
sequence,
},
idleInteractivities: [],
};
const extraDeck = judgeSelf(controler, state)
? state.meExtraDeck
: state.opExtraDeck;
extendState(extraDeck, newExtraDeck);
});
builder.addCase(fetchExtraDeckMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const meta = action.payload.meta;
const extraDeck = judgeSelf(controler, state)
? state.meExtraDeck
: state.opExtraDeck;
extendMeta(extraDeck, meta, sequence);
});
};
// 删除额外卡组
export const removeExtraDeckImpl: DuelReducer<{
controler: number;
sequence: number;
}> = (state, action) => {
const extraDeck = judgeSelf(action.payload.controler, state)
? state.meExtraDeck
: state.opExtraDeck;
removeCard(extraDeck, action.payload.sequence);
};
export const addExtraDeckIdleInteractivitiesImpl: DuelReducer<{
player: number;
sequence: number;
interactivity: Interactivity<number>;
}> = (state, action) => {
const extraDeck = judgeSelf(action.payload.player, state)
? state.meExtraDeck
: state.opExtraDeck;
extendIdleInteractivities(
extraDeck,
action.payload.sequence,
action.payload.interactivity
);
};
export const selectMeExtraDeck = (state: RootState) =>
state.duel.meExtraDeck || { inner: [] };
export const selectOpExtraDeck = (state: RootState) =>
state.duel.opExtraDeck || { inner: [] };
...@@ -18,7 +18,7 @@ export interface DuelFieldState { ...@@ -18,7 +18,7 @@ export interface DuelFieldState {
export interface CardState { export interface CardState {
occupant?: CardMeta; // 占据此位置的卡牌元信息 occupant?: CardMeta; // 占据此位置的卡牌元信息
location: { location: {
controler: number; controler?: number;
location?: number; location?: number;
position?: ygopro.CardPosition; position?: ygopro.CardPosition;
overlay_sequence?: number; overlay_sequence?: number;
......
...@@ -73,6 +73,7 @@ export const removeHandImpl: CaseReducer< ...@@ -73,6 +73,7 @@ export const removeHandImpl: CaseReducer<
removeCard(hands, sequence); removeCard(hands, sequence);
}; };
// 在特定位置增加手牌
export const insertHandMeta = createAsyncMetaThunk("duel/insertHandMeta"); export const insertHandMeta = createAsyncMetaThunk("duel/insertHandMeta");
export const updateHandsMeta = createAsyncRepeatedMetaThunk( export const updateHandsMeta = createAsyncRepeatedMetaThunk(
...@@ -171,8 +172,6 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -171,8 +172,6 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
}); });
}; };
// 在特定位置增加手牌
export const selectMeHands = (state: RootState) => export const selectMeHands = (state: RootState) =>
state.duel.meHands || { inner: [] }; state.duel.meHands || { inner: [] };
export const selectOpHands = (state: RootState) => export const selectOpHands = (state: RootState) =>
......
...@@ -95,6 +95,12 @@ import { ...@@ -95,6 +95,12 @@ import {
clearAllIdleInteractivitiesImpl, clearAllIdleInteractivitiesImpl,
clearAllPlaceInteractivitiesImpl, clearAllPlaceInteractivitiesImpl,
} from "./commonSlice"; } from "./commonSlice";
import {
ExtraDeckState,
extraDeckCase,
removeExtraDeckImpl,
addExtraDeckIdleInteractivitiesImpl,
} from "./extraDeckSlice";
export interface DuelState { export interface DuelState {
selfType?: number; selfType?: number;
...@@ -119,6 +125,9 @@ export interface DuelState { ...@@ -119,6 +125,9 @@ export interface DuelState {
meDeck?: DeckState; // 自己的卡组状态 meDeck?: DeckState; // 自己的卡组状态
opDeck?: DeckState; // 对手的卡组状态 opDeck?: DeckState; // 对手的卡组状态
meExtraDeck?: ExtraDeckState; // 自己的额外卡组状态
opExtraDeck?: ExtraDeckState; // 对手的额外卡组状态
meTimeLimit?: TimeLimit; // 自己的计时 meTimeLimit?: TimeLimit; // 自己的计时
opTimeLimit?: TimeLimit; // 对手的计时 opTimeLimit?: TimeLimit; // 对手的计时
...@@ -199,6 +208,10 @@ const duelSlice = createSlice({ ...@@ -199,6 +208,10 @@ const duelSlice = createSlice({
// 卡组相关`Reducer` // 卡组相关`Reducer`
initDeck: initDeckImpl, initDeck: initDeckImpl,
// 额外卡组相关`Reducer`
removeExtraDeck: removeExtraDeckImpl,
addExtraDeckIdleInteractivities: addExtraDeckIdleInteractivitiesImpl,
// 阶段相关 // 阶段相关
updatePhase: newPhaseImpl, updatePhase: newPhaseImpl,
setEnableBp: setEnableBpImpl, setEnableBp: setEnableBpImpl,
...@@ -242,6 +255,7 @@ const duelSlice = createSlice({ ...@@ -242,6 +255,7 @@ const duelSlice = createSlice({
magicCase(builder); magicCase(builder);
cemeteryCase(builder); cemeteryCase(builder);
exclusionCase(builder); exclusionCase(builder);
extraDeckCase(builder);
checkCardModalCase(builder); checkCardModalCase(builder);
YesNoModalCase(builder); YesNoModalCase(builder);
optionModalCase(builder); optionModalCase(builder);
...@@ -297,6 +311,8 @@ export const { ...@@ -297,6 +311,8 @@ export const {
setOptionModalIsOpen, setOptionModalIsOpen,
resetOptionModal, resetOptionModal,
initDeck, initDeck,
removeExtraDeck,
addExtraDeckIdleInteractivities,
initExclusion, initExclusion,
removeExclusion, removeExclusion,
addExclusionIdleInteractivities, addExclusionIdleInteractivities,
......
...@@ -5,6 +5,7 @@ import { fetchMonsterMeta } from "../../reducers/duel/monstersSlice"; ...@@ -5,6 +5,7 @@ import { fetchMonsterMeta } from "../../reducers/duel/monstersSlice";
import { import {
removeCemetery, removeCemetery,
removeExclusion, removeExclusion,
removeExtraDeck,
removeHand, removeHand,
removeMagic, removeMagic,
removeMonster, removeMonster,
...@@ -13,6 +14,7 @@ import { fetchMagicMeta } from "../../reducers/duel/magicSlice"; ...@@ -13,6 +14,7 @@ import { fetchMagicMeta } from "../../reducers/duel/magicSlice";
import { fetchCemeteryMeta } from "../../reducers/duel/cemeretySlice"; import { fetchCemeteryMeta } from "../../reducers/duel/cemeretySlice";
import { insertHandMeta } from "../../reducers/duel/handsSlice"; import { insertHandMeta } from "../../reducers/duel/handsSlice";
import { fetchExclusionMeta } from "../../reducers/duel/exclusionSlice"; import { fetchExclusionMeta } from "../../reducers/duel/exclusionSlice";
import { fetchExtraDeckMeta } from "../../reducers/duel/extraDeckSlice";
export default (move: MsgMove, dispatch: AppDispatch) => { export default (move: MsgMove, dispatch: AppDispatch) => {
const code = move.code; const code = move.code;
...@@ -54,6 +56,13 @@ export default (move: MsgMove, dispatch: AppDispatch) => { ...@@ -54,6 +56,13 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
break; break;
} }
case ygopro.CardZone.EXTRA: {
dispatch(
removeExtraDeck({ controler: from.controler, sequence: from.sequence })
);
break;
}
default: { default: {
console.log(`Unhandled zone type ${from.location}`); console.log(`Unhandled zone type ${from.location}`);
break; break;
...@@ -114,6 +123,17 @@ export default (move: MsgMove, dispatch: AppDispatch) => { ...@@ -114,6 +123,17 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
break; break;
} }
case ygopro.CardZone.EXTRA: {
dispatch(
fetchExtraDeckMeta({
controler: to.controler,
sequence: to.sequence,
code,
})
);
break;
}
default: { default: {
console.log(`Unhandled zone type ${to.location}`); console.log(`Unhandled zone type ${to.location}`);
......
...@@ -10,6 +10,7 @@ import { ...@@ -10,6 +10,7 @@ import {
addCemeteryIdleInteractivities, addCemeteryIdleInteractivities,
clearAllIdleInteractivities, clearAllIdleInteractivities,
addExclusionIdleInteractivities, addExclusionIdleInteractivities,
addExtraDeckIdleInteractivities,
} from "../../reducers/duel/mod"; } from "../../reducers/duel/mod";
import MsgSelectIdleCmd = ygopro.StocGameMessage.MsgSelectIdleCmd; import MsgSelectIdleCmd = ygopro.StocGameMessage.MsgSelectIdleCmd;
import { ActionCreatorWithPayload } from "@reduxjs/toolkit"; import { ActionCreatorWithPayload } from "@reduxjs/toolkit";
...@@ -91,6 +92,11 @@ export default (selectIdleCmd: MsgSelectIdleCmd, dispatch: AppDispatch) => { ...@@ -91,6 +92,11 @@ export default (selectIdleCmd: MsgSelectIdleCmd, dispatch: AppDispatch) => {
break; break;
} }
case ygopro.CardZone.EXTRA: {
dispatcher(data, interactType, addExtraDeckIdleInteractivities);
break;
}
default: { default: {
console.log(`Unhandled zone type: ${cardInfo.location}`); console.log(`Unhandled zone type: ${cardInfo.location}`);
} }
......
...@@ -4,13 +4,6 @@ import { useAppSelector } from "../../hook"; ...@@ -4,13 +4,6 @@ import { useAppSelector } from "../../hook";
import { selectMeDeck, selectOpDeck } from "../../reducers/duel/deckSlice"; import { selectMeDeck, selectOpDeck } from "../../reducers/duel/deckSlice";
import SingleSlot, { Depth } from "./singleSlot"; import SingleSlot, { Depth } from "./singleSlot";
const Deck = () => (
<>
<CommonDeck />
<ExtraDeck />
</>
);
const CommonDeck = () => { const CommonDeck = () => {
const meDeck = useAppSelector(selectMeDeck).inner; const meDeck = useAppSelector(selectMeDeck).inner;
const opDeck = useAppSelector(selectOpDeck).inner; const opDeck = useAppSelector(selectOpDeck).inner;
...@@ -31,32 +24,6 @@ const CommonDeck = () => { ...@@ -31,32 +24,6 @@ const CommonDeck = () => {
); );
}; };
const ExtraDeck = () => {
const shape = CONFIG.ExtraDeckSlotShape();
const position = new BABYLON.Vector3(
-3.3,
shape.depth / 2 + CONFIG.Floating,
-3.3
);
const rotation = CONFIG.DeckSlotRotation();
return (
<box
name="extra-deck"
width={shape.width}
height={shape.height}
depth={shape.depth}
position={position}
rotation={rotation}
>
<standardMaterial
name="extra-deck-mat"
diffuseColor={CONFIG.ExtraDeckColor()}
/>
</box>
);
};
const deckPosition = (player: number, deckLength: number) => { const deckPosition = (player: number, deckLength: number) => {
const x = player == 0 ? 3.2 : -3.2; const x = player == 0 ? 3.2 : -3.2;
const y = (Depth * deckLength) / 2 + CONFIG.Floating; const y = (Depth * deckLength) / 2 + CONFIG.Floating;
...@@ -65,4 +32,4 @@ const deckPosition = (player: number, deckLength: number) => { ...@@ -65,4 +32,4 @@ const deckPosition = (player: number, deckLength: number) => {
return new BABYLON.Vector3(x, y, z); return new BABYLON.Vector3(x, y, z);
}; };
export default Deck; export default CommonDeck;
import SingleSlot, { Depth } from "./singleSlot";
import * as BABYLON from "@babylonjs/core";
import * as CONFIG from "../../config/ui";
import { useAppSelector } from "../../hook";
import {
selectMeExtraDeck,
selectOpExtraDeck,
} from "../../reducers/duel/extraDeckSlice";
const ExtraDeck = () => {
const meExtraDeck = useAppSelector(selectMeExtraDeck).inner;
const opExtraDeck = useAppSelector(selectOpExtraDeck).inner;
return (
<>
<SingleSlot
state={meExtraDeck}
position={extraDeckPosition(0, meExtraDeck.length)}
rotation={CONFIG.CardSlotRotation(false)}
/>
<SingleSlot
state={opExtraDeck}
position={extraDeckPosition(1, opExtraDeck.length)}
rotation={CONFIG.CardSlotRotation(true)}
/>
</>
);
};
const extraDeckPosition = (player: number, deckLength: number) => {
const x = player == 0 ? -3.3 : 3.3;
const y = (Depth & deckLength) / 2 + CONFIG.Floating;
const z = player == 0 ? -3.3 : 3.3;
return new BABYLON.Vector3(x, y, z);
};
export default ExtraDeck;
...@@ -9,7 +9,7 @@ import CardModal from "./cardModal"; ...@@ -9,7 +9,7 @@ import CardModal from "./cardModal";
import HintNotification from "./hintNotification"; import HintNotification from "./hintNotification";
import Magics from "./magics"; import Magics from "./magics";
import Field from "./field"; import Field from "./field";
import Deck from "./deck"; import CommonDeck from "./deck";
import Exclusion from "./exclusion"; import Exclusion from "./exclusion";
import Cemeteries from "./cemetery"; import Cemeteries from "./cemetery";
import CardListModal from "./cardListModal"; import CardListModal from "./cardListModal";
...@@ -19,6 +19,7 @@ import PositionModal from "./positionModal"; ...@@ -19,6 +19,7 @@ import PositionModal from "./positionModal";
import OptionModal from "./optionModal"; import OptionModal from "./optionModal";
import Phase from "./phase"; import Phase from "./phase";
import CheckCardModalV2 from "./checkCardModalV2"; import CheckCardModalV2 from "./checkCardModalV2";
import ExtraDeck from "./extraDeck";
// Ref: https://github.com/brianzinn/react-babylonjs/issues/126 // Ref: https://github.com/brianzinn/react-babylonjs/issues/126
const NeosDuel = () => ( const NeosDuel = () => (
...@@ -34,7 +35,8 @@ const NeosDuel = () => ( ...@@ -34,7 +35,8 @@ const NeosDuel = () => (
<Monsters /> <Monsters />
<Magics /> <Magics />
<Field /> <Field />
<Deck /> <CommonDeck />
<ExtraDeck />
<Cemeteries /> <Cemeteries />
<Exclusion /> <Exclusion />
<Field /> <Field />
......
...@@ -23,6 +23,8 @@ import { ...@@ -23,6 +23,8 @@ import {
import socketMiddleWare, { socketCmd } from "../middleware/socket"; import socketMiddleWare, { socketCmd } from "../middleware/socket";
import sqliteMiddleWare, { sqliteCmd } from "../middleware/sqlite"; import sqliteMiddleWare, { sqliteCmd } from "../middleware/sqlite";
import { Button } from "antd"; import { Button } from "antd";
import { store } from "../store";
import { initMeExtraDeckMeta } from "../reducers/duel/extraDeckSlice";
const READY_STATE = "ready"; const READY_STATE = "ready";
...@@ -56,6 +58,7 @@ export default function WaitRoom() { ...@@ -56,6 +58,7 @@ export default function WaitRoom() {
}); });
}, []); }, []);
const dispatch = store.dispatch;
const joined = useAppSelector(selectJoined); const joined = useAppSelector(selectJoined);
const chat = useAppSelector(selectChat); const chat = useAppSelector(selectChat);
const isHost = useAppSelector(selectIsHost); const isHost = useAppSelector(selectIsHost);
...@@ -67,6 +70,7 @@ export default function WaitRoom() { ...@@ -67,6 +70,7 @@ export default function WaitRoom() {
const deck = await fetchDeck("hero.ydk"); const deck = await fetchDeck("hero.ydk");
sendUpdateDeck(deck); sendUpdateDeck(deck);
dispatch(initMeExtraDeckMeta({ controler: 0, codes: deck.extra || [] }));
setChoseDeck(true); setChoseDeck(true);
}; };
......
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