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";
import adaptSelectPlaceResponse from "./selectPlace";
import adaptSelectCardResponse from "./selectCard";
import adaptSelectChainResponse from "./selectChain";
import adaptSelectEffectYnResponse from "./selectEffectYn";
/*
* CTOS CTOS_RESPONSE
......@@ -40,6 +41,11 @@ export default class CtosResponsePacket extends YgoProPacket {
break;
}
case "select_effect_yn": {
extraData = adaptSelectEffectYnResponse(response.select_effect_yn);
break;
}
default: {
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;
export const MSG_MOVE = 50;
export const MSG_SELECT_CARD = 15;
export const MSG_SELECT_CHAIN = 16;
export const MSG_SELECT_EFFECTYN = 12;
......@@ -16,6 +16,7 @@ import MsgSelectPlaceAdapter from "./selectPlace";
import MsgMoveAdapter from "./move";
import MsgSelectCardAdapter from "./selectCard";
import MsgSelectChainAdapter from "./selectChain";
import MsgSelectEffectYnAdapter from "./selectEffectYn";
/*
* STOC GameMsg
......@@ -91,6 +92,11 @@ export default class GameMsgAdapter implements StocAdapter {
break;
}
case GAME_MSG.MSG_SELECT_EFFECTYN: {
gameMsg.select_effect_yn = MsgSelectEffectYnAdapter(gameData);
break;
}
default: {
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) {
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 {
setCheckCardMOdalCancelAbleImpl,
setCheckCardModalCancelResponseImpl,
resetCheckCardModalImpl,
setYesNoModalIsOpenImpl,
checkCardModalCase,
YesNoModalCase,
} from "./modalSlice";
import {
MonsterState,
......@@ -84,6 +86,7 @@ const initialState: DuelState = {
cardModal: { isOpen: false, interactivies: [] },
cardListModal: { isOpen: false, list: [] },
checkCardModal: { isOpen: false, cancelAble: false, tags: [] },
yesNoModal: { isOpen: false },
},
};
......@@ -130,6 +133,7 @@ const duelSlice = createSlice({
setCheckCardMOdalCancelAble: setCheckCardMOdalCancelAbleImpl,
setCheckCardModalCancelResponse: setCheckCardModalCancelResponseImpl,
resetCheckCardModal: resetCheckCardModalImpl,
setYesNoModalIsOpen: setYesNoModalIsOpenImpl,
},
extraReducers(builder) {
handsCase(builder);
......@@ -138,6 +142,7 @@ const duelSlice = createSlice({
magicCase(builder);
cemeteryCase(builder);
checkCardModalCase(builder);
YesNoModalCase(builder);
},
});
......@@ -169,6 +174,7 @@ export const {
setCheckCardMOdalCancelAble,
setCheckCardModalCancelResponse,
resetCheckCardModal,
setYesNoModalIsOpen,
} = duelSlice.actions;
export const selectDuelHsStart = (state: RootState) => {
return state.duel.meInitInfo != null;
......
......@@ -4,7 +4,9 @@ import {
createAsyncThunk,
ActionReducerMapBuilder,
} 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 { DuelState } from "./mod";
import { judgeSelf } from "./util";
......@@ -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) => {
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) =>
state.duel.modalState.cardModal.isOpen;
export const selectCardModalName = (state: RootState) =>
......@@ -257,3 +299,7 @@ export const selectCheckCardModalCancelAble = (state: RootState) =>
state.duel.modalState.checkCardModal.cancelAble;
export const selectCheckCardModalCacnelResponse = (state: RootState) =>
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";
import onMsgMove from "./move";
import onMsgSelectCard from "./selectCard";
import onMsgSelectChain from "./selectChain";
import onMsgSelectEffectYn from "./selectEffectYn";
export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
......@@ -18,74 +19,70 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
switch (msg.gameMsg) {
case "start": {
const start = msg.start;
onMsgStart(start, dispatch);
break;
}
case "draw": {
const draw = msg.draw;
onMsgDraw(draw, dispatch);
break;
}
case "new_turn": {
const newTurn = msg.new_turn;
onMsgNewTurn(newTurn, dispatch);
break;
}
case "new_phase": {
const newPhase = msg.new_phase;
onMsgNewPhase(newPhase, dispatch);
break;
}
case "hint": {
const hint = msg.hint;
onMsgHint(hint, dispatch);
break;
}
case "select_idle_cmd": {
const selectIdleCmd = msg.select_idle_cmd;
onMsgSelectIdleCmd(selectIdleCmd, dispatch);
break;
}
case "select_place": {
const selectPlace = msg.select_place;
onMsgSelectPlace(selectPlace, dispatch);
break;
}
case "move": {
const move = msg.move;
onMsgMove(move, dispatch);
break;
}
case "select_card": {
const selectCard = msg.select_card;
onMsgSelectCard(selectCard, dispatch);
break;
}
case "select_chain": {
const selectChain = msg.select_chain;
onMsgSelectChain(selectChain, dispatch);
break;
}
case "select_effect_yn": {
const selectEffectYn = msg.select_effect_yn;
onMsgSelectEffectYn(selectEffectYn, dispatch);
break;
}
default: {
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";
import Cemeteries from "./cemetery";
import CardListModal from "./cardListModal";
import CheckCardModal from "./checkCardModal";
import YesNoModal from "./yesNoModal";
// Ref: https://github.com/brianzinn/react-babylonjs/issues/126
const NeosDuel = () => (
......@@ -42,6 +43,7 @@ const NeosDuel = () => (
<CardListModal />
<HintNotification />
<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