Commit e0e04dcf authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/service/select_effect_yn' into 'main'

Feat/service/select effect yn

See merge request mycard/Neos!66
parents 9c34b487 2661de6f
neos-protobuf @ d8991a3d
Subproject commit 779d9d9c18d393df432570d80753a6fd9134c21e Subproject commit d8991a3d4a3b96db852653d5eb49664826466c90
This diff is collapsed.
...@@ -5,6 +5,7 @@ import adaptSelectIdleCmdResponse from "./selectIdleCmd"; ...@@ -5,6 +5,7 @@ import adaptSelectIdleCmdResponse from "./selectIdleCmd";
import adaptSelectPlaceResponse from "./selectPlace"; import adaptSelectPlaceResponse from "./selectPlace";
import adaptSelectCardResponse from "./selectCard"; import adaptSelectCardResponse from "./selectCard";
import adaptSelectChainResponse from "./selectChain"; import adaptSelectChainResponse from "./selectChain";
import adaptSelectEffectYnResponse from "./selectEffectYn";
/* /*
* CTOS CTOS_RESPONSE * CTOS CTOS_RESPONSE
...@@ -40,6 +41,11 @@ export default class CtosResponsePacket extends YgoProPacket { ...@@ -40,6 +41,11 @@ export default class CtosResponsePacket extends YgoProPacket {
break; break;
} }
case "select_effect_yn": {
extraData = adaptSelectEffectYnResponse(response.select_effect_yn);
break;
}
default: { default: {
break; break;
} }
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "../../bufferIO";
export default (
response: ygopro.CtosGameMsgResponse.SelectEffectYnResponse
) => {
const array = new Uint8Array(4);
const writer = new BufferWriter(array, true);
writer.writeUint32(response.selected ? 1 : 0);
return array;
};
...@@ -36,3 +36,4 @@ export const MSG_SELECT_PLACE = 18; ...@@ -36,3 +36,4 @@ export const MSG_SELECT_PLACE = 18;
export const MSG_MOVE = 50; export const MSG_MOVE = 50;
export const MSG_SELECT_CARD = 15; export const MSG_SELECT_CARD = 15;
export const MSG_SELECT_CHAIN = 16; export const MSG_SELECT_CHAIN = 16;
export const MSG_SELECT_EFFECTYN = 12;
...@@ -16,6 +16,7 @@ import MsgSelectPlaceAdapter from "./selectPlace"; ...@@ -16,6 +16,7 @@ import MsgSelectPlaceAdapter from "./selectPlace";
import MsgMoveAdapter from "./move"; import MsgMoveAdapter from "./move";
import MsgSelectCardAdapter from "./selectCard"; import MsgSelectCardAdapter from "./selectCard";
import MsgSelectChainAdapter from "./selectChain"; import MsgSelectChainAdapter from "./selectChain";
import MsgSelectEffectYnAdapter from "./selectEffectYn";
/* /*
* STOC GameMsg * STOC GameMsg
...@@ -91,6 +92,11 @@ export default class GameMsgAdapter implements StocAdapter { ...@@ -91,6 +92,11 @@ export default class GameMsgAdapter implements StocAdapter {
break; break;
} }
case GAME_MSG.MSG_SELECT_EFFECTYN: {
gameMsg.select_effect_yn = MsgSelectEffectYnAdapter(gameData);
break;
}
default: { default: {
console.log("Unhandled GameMessage function=", func); console.log("Unhandled GameMessage function=", func);
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReader } from "../../bufferIO";
import MsgSelectEffectYn = ygopro.StocGameMessage.MsgSelectEffectYn;
/*
* Msg Select EffectYn
*
* @param - see: https://code.mycard.moe/mycard/neos-protobuf/-/blob/main/idl/ocgcore.neos-protobuf
* @usage - 玩家选择是否发动效果
*
* */
export default (data: Uint8Array) => {
const reader = new BufferReader(data, true);
const player = reader.readUint8();
const code = reader.readUint32();
const location = reader.readCardLocation();
const effect_description = reader.readUint32();
return new MsgSelectEffectYn({
player,
code,
location,
effect_description,
});
};
...@@ -176,3 +176,16 @@ export function sendSelectChainResponse(value: number) { ...@@ -176,3 +176,16 @@ export function sendSelectChainResponse(value: number) {
socketMiddleWare({ cmd: socketCmd.SEND, payload }); socketMiddleWare({ cmd: socketCmd.SEND, payload });
} }
export function sendSelectEffectYnResponse(value: boolean) {
const response = new ygopro.YgoCtosMsg({
ctos_response: new ygopro.CtosGameMsgResponse({
select_effect_yn: new ygopro.CtosGameMsgResponse.SelectEffectYnResponse({
selected: value,
}),
}),
});
const payload = new GameMsgResponse(response).serialize();
socketMiddleWare({ cmd: socketCmd.SEND, payload });
}
...@@ -31,7 +31,9 @@ import { ...@@ -31,7 +31,9 @@ import {
setCheckCardMOdalCancelAbleImpl, setCheckCardMOdalCancelAbleImpl,
setCheckCardModalCancelResponseImpl, setCheckCardModalCancelResponseImpl,
resetCheckCardModalImpl, resetCheckCardModalImpl,
setYesNoModalIsOpenImpl,
checkCardModalCase, checkCardModalCase,
YesNoModalCase,
} from "./modalSlice"; } from "./modalSlice";
import { import {
MonsterState, MonsterState,
...@@ -84,6 +86,7 @@ const initialState: DuelState = { ...@@ -84,6 +86,7 @@ const initialState: DuelState = {
cardModal: { isOpen: false, interactivies: [] }, cardModal: { isOpen: false, interactivies: [] },
cardListModal: { isOpen: false, list: [] }, cardListModal: { isOpen: false, list: [] },
checkCardModal: { isOpen: false, cancelAble: false, tags: [] }, checkCardModal: { isOpen: false, cancelAble: false, tags: [] },
yesNoModal: { isOpen: false },
}, },
}; };
...@@ -130,6 +133,7 @@ const duelSlice = createSlice({ ...@@ -130,6 +133,7 @@ const duelSlice = createSlice({
setCheckCardMOdalCancelAble: setCheckCardMOdalCancelAbleImpl, setCheckCardMOdalCancelAble: setCheckCardMOdalCancelAbleImpl,
setCheckCardModalCancelResponse: setCheckCardModalCancelResponseImpl, setCheckCardModalCancelResponse: setCheckCardModalCancelResponseImpl,
resetCheckCardModal: resetCheckCardModalImpl, resetCheckCardModal: resetCheckCardModalImpl,
setYesNoModalIsOpen: setYesNoModalIsOpenImpl,
}, },
extraReducers(builder) { extraReducers(builder) {
handsCase(builder); handsCase(builder);
...@@ -138,6 +142,7 @@ const duelSlice = createSlice({ ...@@ -138,6 +142,7 @@ const duelSlice = createSlice({
magicCase(builder); magicCase(builder);
cemeteryCase(builder); cemeteryCase(builder);
checkCardModalCase(builder); checkCardModalCase(builder);
YesNoModalCase(builder);
}, },
}); });
...@@ -169,6 +174,7 @@ export const { ...@@ -169,6 +174,7 @@ export const {
setCheckCardMOdalCancelAble, setCheckCardMOdalCancelAble,
setCheckCardModalCancelResponse, setCheckCardModalCancelResponse,
resetCheckCardModal, resetCheckCardModal,
setYesNoModalIsOpen,
} = duelSlice.actions; } = duelSlice.actions;
export const selectDuelHsStart = (state: RootState) => { export const selectDuelHsStart = (state: RootState) => {
return state.duel.meInitInfo != null; return state.duel.meInitInfo != null;
......
...@@ -4,7 +4,9 @@ import { ...@@ -4,7 +4,9 @@ import {
createAsyncThunk, createAsyncThunk,
ActionReducerMapBuilder, ActionReducerMapBuilder,
} from "@reduxjs/toolkit"; } from "@reduxjs/toolkit";
import { fetchCard, getCardStr } from "../../api/cards"; import { CardMeta, fetchCard, getCardStr } from "../../api/cards";
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { fetchStrings } from "../../api/strings";
import { RootState } from "../../store"; import { RootState } from "../../store";
import { DuelState } from "./mod"; import { DuelState } from "./mod";
import { judgeSelf } from "./util"; import { judgeSelf } from "./util";
...@@ -46,6 +48,11 @@ export interface ModalState { ...@@ -46,6 +48,11 @@ export interface ModalState {
}[]; }[];
}[]; }[];
}; };
// Yes or No弹窗
yesNoModal: {
isOpen: boolean;
msg?: string;
};
} }
// 更新卡牌弹窗打开状态 // 更新卡牌弹窗打开状态
...@@ -227,6 +234,41 @@ export const resetCheckCardModalImpl: CaseReducer<DuelState> = (state) => { ...@@ -227,6 +234,41 @@ export const resetCheckCardModalImpl: CaseReducer<DuelState> = (state) => {
state.modalState.checkCardModal.tags = []; state.modalState.checkCardModal.tags = [];
}; };
// 更新YesNo弹窗是否打开状态
export const setYesNoModalIsOpenImpl: CaseReducer<
DuelState,
PayloadAction<boolean>
> = (state, action) => {
state.modalState.yesNoModal.isOpen = action.payload;
};
// 设置YesNo弹窗展示内容
export const fetchYesNoMeta = createAsyncThunk(
"duel/fetchYesNoMeta",
async (param: {
code: number;
location: ygopro.CardLocation;
descCode: number;
textGenerator: (
desc: string,
cardMeta: CardMeta,
cardLocation: ygopro.CardLocation
) => string;
}) => {
const desc = await fetchStrings("!system", param.descCode);
const meta = await fetchCard(param.code);
// TODO: 国际化文案
return param.textGenerator(desc, meta, param.location);
}
);
export const YesNoModalCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchYesNoMeta.fulfilled, (state, action) => {
state.modalState.yesNoModal.msg = action.payload;
});
};
export const selectCardModalIsOpen = (state: RootState) => export const selectCardModalIsOpen = (state: RootState) =>
state.duel.modalState.cardModal.isOpen; state.duel.modalState.cardModal.isOpen;
export const selectCardModalName = (state: RootState) => export const selectCardModalName = (state: RootState) =>
...@@ -257,3 +299,7 @@ export const selectCheckCardModalCancelAble = (state: RootState) => ...@@ -257,3 +299,7 @@ export const selectCheckCardModalCancelAble = (state: RootState) =>
state.duel.modalState.checkCardModal.cancelAble; state.duel.modalState.checkCardModal.cancelAble;
export const selectCheckCardModalCacnelResponse = (state: RootState) => export const selectCheckCardModalCacnelResponse = (state: RootState) =>
state.duel.modalState.checkCardModal.cancelResponse; state.duel.modalState.checkCardModal.cancelResponse;
export const selectYesNoModalIsOpen = (state: RootState) =>
state.duel.modalState.yesNoModal.isOpen;
export const selectYesNOModalMsg = (state: RootState) =>
state.duel.modalState.yesNoModal.msg;
...@@ -10,6 +10,7 @@ import onMsgSelectPlace from "./selectPlace"; ...@@ -10,6 +10,7 @@ import onMsgSelectPlace from "./selectPlace";
import onMsgMove from "./move"; import onMsgMove from "./move";
import onMsgSelectCard from "./selectCard"; import onMsgSelectCard from "./selectCard";
import onMsgSelectChain from "./selectChain"; import onMsgSelectChain from "./selectChain";
import onMsgSelectEffectYn from "./selectEffectYn";
export default function handleGameMsg(pb: ygopro.YgoStocMsg) { export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch; const dispatch = store.dispatch;
...@@ -18,74 +19,70 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) { ...@@ -18,74 +19,70 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
switch (msg.gameMsg) { switch (msg.gameMsg) {
case "start": { case "start": {
const start = msg.start; const start = msg.start;
onMsgStart(start, dispatch); onMsgStart(start, dispatch);
break; break;
} }
case "draw": { case "draw": {
const draw = msg.draw; const draw = msg.draw;
onMsgDraw(draw, dispatch); onMsgDraw(draw, dispatch);
break; break;
} }
case "new_turn": { case "new_turn": {
const newTurn = msg.new_turn; const newTurn = msg.new_turn;
onMsgNewTurn(newTurn, dispatch); onMsgNewTurn(newTurn, dispatch);
break; break;
} }
case "new_phase": { case "new_phase": {
const newPhase = msg.new_phase; const newPhase = msg.new_phase;
onMsgNewPhase(newPhase, dispatch); onMsgNewPhase(newPhase, dispatch);
break; break;
} }
case "hint": { case "hint": {
const hint = msg.hint; const hint = msg.hint;
onMsgHint(hint, dispatch); onMsgHint(hint, dispatch);
break; break;
} }
case "select_idle_cmd": { case "select_idle_cmd": {
const selectIdleCmd = msg.select_idle_cmd; const selectIdleCmd = msg.select_idle_cmd;
onMsgSelectIdleCmd(selectIdleCmd, dispatch); onMsgSelectIdleCmd(selectIdleCmd, dispatch);
break; break;
} }
case "select_place": { case "select_place": {
const selectPlace = msg.select_place; const selectPlace = msg.select_place;
onMsgSelectPlace(selectPlace, dispatch); onMsgSelectPlace(selectPlace, dispatch);
break; break;
} }
case "move": { case "move": {
const move = msg.move; const move = msg.move;
onMsgMove(move, dispatch); onMsgMove(move, dispatch);
break; break;
} }
case "select_card": { case "select_card": {
const selectCard = msg.select_card; const selectCard = msg.select_card;
onMsgSelectCard(selectCard, dispatch); onMsgSelectCard(selectCard, dispatch);
break; break;
} }
case "select_chain": { case "select_chain": {
const selectChain = msg.select_chain; const selectChain = msg.select_chain;
onMsgSelectChain(selectChain, dispatch); onMsgSelectChain(selectChain, dispatch);
break; break;
} }
case "select_effect_yn": {
const selectEffectYn = msg.select_effect_yn;
onMsgSelectEffectYn(selectEffectYn, dispatch);
break;
}
default: { default: {
break; break;
} }
......
import { CardMeta } from "../../api/cards";
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { setYesNoModalIsOpen } from "../../reducers/duel/mod";
import { fetchYesNoMeta } from "../../reducers/duel/modalSlice";
import { AppDispatch } from "../../store";
import { CardZoneToChinese } from "./util";
import MsgSelectEffectYn = ygopro.StocGameMessage.MsgSelectEffectYn;
export default (selectEffectYn: MsgSelectEffectYn, dispatch: AppDispatch) => {
const player = selectEffectYn.player;
const code = selectEffectYn.code;
const location = selectEffectYn.location;
const effect_description = selectEffectYn.effect_description;
const textGenerator =
effect_description == 0 || effect_description == 221
? (
desc: string,
cardMeta: CardMeta,
cardLocation: ygopro.CardLocation
) => {
const desc1 = desc.replace(
`[%ls]`,
CardZoneToChinese(cardLocation.location)
);
const desc2 = desc1.replace(`[%ls]`, cardMeta.text.name || "[?]");
return desc2;
}
: (desc: string, cardMeta: CardMeta, _: ygopro.CardLocation) => {
const desc1 = desc.replace(`[%ls]`, cardMeta.text.name || "[?]");
return desc1;
};
dispatch(
fetchYesNoMeta({
code,
location,
descCode: effect_description,
textGenerator,
})
);
dispatch(setYesNoModalIsOpen(true));
};
...@@ -14,6 +14,7 @@ import Exclusion from "./exclusion"; ...@@ -14,6 +14,7 @@ import Exclusion from "./exclusion";
import Cemeteries from "./cemetery"; import Cemeteries from "./cemetery";
import CardListModal from "./cardListModal"; import CardListModal from "./cardListModal";
import CheckCardModal from "./checkCardModal"; import CheckCardModal from "./checkCardModal";
import YesNoModal from "./yesNoModal";
// Ref: https://github.com/brianzinn/react-babylonjs/issues/126 // Ref: https://github.com/brianzinn/react-babylonjs/issues/126
const NeosDuel = () => ( const NeosDuel = () => (
...@@ -42,6 +43,7 @@ const NeosDuel = () => ( ...@@ -42,6 +43,7 @@ const NeosDuel = () => (
<CardListModal /> <CardListModal />
<HintNotification /> <HintNotification />
<CheckCardModal /> <CheckCardModal />
<YesNoModal />
</> </>
); );
......
import React from "react";
import { useAppSelector } from "../../hook";
import { store } from "../../store";
import { Modal, Button } from "antd";
import { sendSelectEffectYnResponse } from "../../api/ocgcore/ocgHelper";
import {
selectYesNoModalIsOpen,
selectYesNOModalMsg,
} from "../../reducers/duel/modalSlice";
import { setYesNoModalIsOpen } from "../../reducers/duel/mod";
const YesNoModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectYesNoModalIsOpen);
const msg = useAppSelector(selectYesNOModalMsg);
return (
<Modal
title={msg}
open={isOpen}
closable={false}
footer={
<>
<Button
onClick={() => {
sendSelectEffectYnResponse(true);
dispatch(setYesNoModalIsOpen(false));
}}
>
Yes
</Button>
<Button
onClick={() => {
sendSelectEffectYnResponse(false);
dispatch(setYesNoModalIsOpen(false));
}}
>
No
</Button>
</>
}
/>
);
};
export default YesNoModal;
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