Commit 451e94e1 authored by Chunchi Che's avatar Chunchi Che

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

Feat/service/move

See merge request mycard/Neos!54
parents 890d930a cdf8a91f
neos-protobuf @ 621489ac
Subproject commit 8cfeb7ac4563b27f2f56e36b53b1453a953d9a9c Subproject commit 621489ace4b9d2ecb653fe0da25279b68154d206
This diff is collapsed.
...@@ -33,3 +33,4 @@ export const MSG_NEW_PHASE = 41; ...@@ -33,3 +33,4 @@ export const MSG_NEW_PHASE = 41;
export const MSG_HINT = 2; export const MSG_HINT = 2;
export const MSG_SELECT_IDLE_CMD = 11; export const MSG_SELECT_IDLE_CMD = 11;
export const MSG_SELECT_PLACE = 18; export const MSG_SELECT_PLACE = 18;
export const MSG_MOVE = 50;
...@@ -13,6 +13,7 @@ import MsgNewPhaseAdapter from "./newPhase"; ...@@ -13,6 +13,7 @@ import MsgNewPhaseAdapter from "./newPhase";
import MsgHintAdapter from "./hint"; import MsgHintAdapter from "./hint";
import MsgSelectIdleCmdAdapter from "./selectIdleCmd"; import MsgSelectIdleCmdAdapter from "./selectIdleCmd";
import MsgSelectPlaceAdapter from "./selectPlace"; import MsgSelectPlaceAdapter from "./selectPlace";
import MsgMoveAdapter from "./move";
/* /*
* STOC GameMsg * STOC GameMsg
...@@ -73,6 +74,11 @@ export default class GameMsgAdapter implements StocAdapter { ...@@ -73,6 +74,11 @@ export default class GameMsgAdapter implements StocAdapter {
break; break;
} }
case GAME_MSG.MSG_MOVE: {
gameMsg.move = MsgMoveAdapter(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 { cardZoneToNumber, numberToCardZone } from "../../util";
import MsgMove = ygopro.StocGameMessage.MsgMove;
/*
* Msg Move
* @param - TODO
*
* @usage - 服务端告知前端/客户端卡牌移动信息
* */
export default (data: Uint8Array) => {
const reader = new BufferReader(data, true);
const code = reader.readUint32();
const readCardLocation = () => {
const controler = reader.readUint8();
const location = reader.readUint8();
const sequence = reader.readUint8();
const ss = reader.readUint8();
const cardLocation = new ygopro.CardLocation({
controler,
location: numberToCardZone(location),
sequence,
});
if (location != cardZoneToNumber(ygopro.CardZone.OVERLAY)) {
cardLocation.position = ss;
} else {
cardLocation.overlay_sequence = ss;
}
return cardLocation;
};
const fromLocation = readCardLocation();
const toLocation = readCardLocation();
return new MsgMove({
code,
from: fromLocation,
to: toLocation,
reason: reader.readUint8(),
});
};
...@@ -113,3 +113,46 @@ export function cardZoneToNumber(zone: ygopro.CardZone): number { ...@@ -113,3 +113,46 @@ export function cardZoneToNumber(zone: ygopro.CardZone): number {
} }
} }
} }
export function numberToCardZone(
location: number
): ygopro.CardZone | undefined {
switch (location) {
case 0x01: {
return ygopro.CardZone.DECK;
}
case 0x02: {
return ygopro.CardZone.HAND;
}
case 0x04: {
return ygopro.CardZone.MZONE;
}
case 0x08: {
return ygopro.CardZone.SZONE;
}
case 0x10: {
return ygopro.CardZone.GRAVE;
}
case 0x20: {
return ygopro.CardZone.REMOVED;
}
case 0x40: {
return ygopro.CardZone.EXTRA;
}
case 0x80: {
return ygopro.CardZone.OVERLAY;
}
case 0x0c: {
return ygopro.CardZone.ONFIELD;
}
case 0x100: {
return ygopro.CardZone.FZONE;
}
case 0x200: {
return ygopro.CardZone.PZONE;
}
default: {
return undefined;
}
}
}
...@@ -7,12 +7,12 @@ import { ...@@ -7,12 +7,12 @@ import {
import { DuelState } from "./mod"; 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, Card, Interactivity } from "./util"; import { judgeSelf, Hand, Interactivity } from "./util";
import * as UICONFIG from "../../config/ui"; import * as UICONFIG from "../../config/ui";
export interface Hands { export interface Hands {
// 注意:手牌的位置顺序是有约束的 // 注意:手牌的位置顺序是有约束的
cards: Card[]; cards: Hand[];
} }
// 增加手牌 // 增加手牌
...@@ -86,7 +86,7 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -86,7 +86,7 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
// 更新手牌的位置和旋转信息 // 更新手牌的位置和旋转信息
// //
// TODO: 兼容对方手牌 // TODO: 兼容对方手牌
function setHandsTransform(hands: Card[]): void { function setHandsTransform(hands: Hand[]): void {
const groundShape = UICONFIG.GroundShape(); const groundShape = UICONFIG.GroundShape();
const handShape = UICONFIG.HandShape(); const handShape = UICONFIG.HandShape();
const gap = groundShape.width / (hands.length - 1); const gap = groundShape.width / (hands.length - 1);
...@@ -140,6 +140,20 @@ export const addHandsInteractivityImpl: CaseReducer< ...@@ -140,6 +140,20 @@ export const addHandsInteractivityImpl: CaseReducer<
} }
}; };
// 删除手牌
export const removeHandImpl: CaseReducer<
DuelState,
PayloadAction<[number, number]>
> = (state, action) => {
const controler = action.payload[0];
const sequence = action.payload[1];
const hands = judgeSelf(controler, state) ? state.meHands : state.opHands;
if (hands) {
hands.cards = hands.cards.filter((_, idx) => idx != sequence);
}
};
export const selectMeHands = (state: RootState) => export const selectMeHands = (state: RootState) =>
state.duel.meHands || { cards: [] }; state.duel.meHands || { cards: [] };
export const selectOpHands = (state: RootState) => export const selectOpHands = (state: RootState) =>
......
import { judgeSelf, Magic, InteractType } from "./util"; import { judgeSelf, Magic, InteractType } from "./util";
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit"; import {
PayloadAction,
CaseReducer,
createAsyncThunk,
ActionReducerMapBuilder,
} 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 { CardMeta, fetchCard } from "../../api/cards";
export interface MagicState { export interface MagicState {
magics: Magic[]; magics: Magic[];
...@@ -74,8 +80,76 @@ export const clearMagicSelectInfoImpl: CaseReducer< ...@@ -74,8 +80,76 @@ export const clearMagicSelectInfoImpl: CaseReducer<
const magics = judgeSelf(player, state) ? state.meMagics : state.opMagics; const magics = judgeSelf(player, state) ? state.meMagics : state.opMagics;
if (magics) { if (magics) {
magics.magics = []; for (const magic of magics.magics) {
magic.selectInfo = undefined;
}
}
};
// 增加魔法陷阱
export const fetchMagicMeta = createAsyncThunk(
"duel/fetchMagicMeta",
async (param: [number, number, number]) => {
const controler = param[0];
const sequence = param[1];
const code = param[2];
const meta = await fetchCard(code);
const response: [number, number, CardMeta] = [controler, sequence, meta];
return response;
} }
);
export const magicCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchMagicMeta.pending, (state, action) => {
// Meta结果没返回之前先更新`ID`
const controler = action.meta.arg[0];
const sequence = action.meta.arg[1];
const code = action.meta.arg[2];
const meta = { id: code, data: {}, text: {} };
if (judgeSelf(controler, state)) {
if (state.meMagics) {
for (const magic of state.meMagics.magics) {
if (magic.sequence == sequence) {
magic.occupant = meta;
}
}
}
} else {
if (state.opMagics) {
for (const magic of state.opMagics.magics) {
if (magic.sequence == sequence) {
magic.occupant = meta;
}
}
}
}
});
builder.addCase(fetchMagicMeta.fulfilled, (state, action) => {
const controler = action.payload[0];
const sequence = action.payload[1];
const meta = action.payload[2];
if (judgeSelf(controler, state)) {
if (state.meMagics) {
for (const magic of state.meMagics.magics) {
if (magic.sequence == sequence) {
magic.occupant = meta;
}
}
}
} else {
if (state.opMagics) {
for (const magic of state.opMagics.magics) {
if (magic.sequence == sequence) {
magic.occupant = meta;
}
}
}
}
});
}; };
export const selectMeMagics = (state: RootState) => export const selectMeMagics = (state: RootState) =>
......
...@@ -11,6 +11,7 @@ import { ...@@ -11,6 +11,7 @@ import {
handsCase, handsCase,
clearHandsInteractivityImpl, clearHandsInteractivityImpl,
addHandsInteractivityImpl, addHandsInteractivityImpl,
removeHandImpl,
} from "./handsSlice"; } from "./handsSlice";
import { newTurnImpl } from "./turnSlice"; import { newTurnImpl } from "./turnSlice";
import { newPhaseImpl } from "./phaseSlice"; import { newPhaseImpl } from "./phaseSlice";
...@@ -28,12 +29,14 @@ import { ...@@ -28,12 +29,14 @@ import {
initMonstersImpl, initMonstersImpl,
addMonsterPlaceSelectAbleImpl, addMonsterPlaceSelectAbleImpl,
clearMonsterSelectInfoImpl, clearMonsterSelectInfoImpl,
monsterCase,
} from "./monstersSlice"; } from "./monstersSlice";
import { import {
MagicState, MagicState,
initMagicsImpl, initMagicsImpl,
addMagicPlaceSelectAbleImpl, addMagicPlaceSelectAbleImpl,
clearMagicSelectInfoImpl, clearMagicSelectInfoImpl,
magicCase,
} from "./magicSlice"; } from "./magicSlice";
export interface DuelState { export interface DuelState {
...@@ -84,6 +87,7 @@ const duelSlice = createSlice({ ...@@ -84,6 +87,7 @@ const duelSlice = createSlice({
// 手牌相关`Reducer` // 手牌相关`Reducer`
clearHandsInteractivity: clearHandsInteractivityImpl, clearHandsInteractivity: clearHandsInteractivityImpl,
addHandsInteractivity: addHandsInteractivityImpl, addHandsInteractivity: addHandsInteractivityImpl,
removeHand: removeHandImpl,
// 怪兽区相关`Reducer` // 怪兽区相关`Reducer`
initMonsters: initMonstersImpl, initMonsters: initMonstersImpl,
...@@ -104,6 +108,8 @@ const duelSlice = createSlice({ ...@@ -104,6 +108,8 @@ const duelSlice = createSlice({
extraReducers(builder) { extraReducers(builder) {
handsCase(builder); handsCase(builder);
hintCase(builder); hintCase(builder);
monsterCase(builder);
magicCase(builder);
}, },
}); });
...@@ -125,6 +131,7 @@ export const { ...@@ -125,6 +131,7 @@ export const {
initMagics, initMagics,
addMagicPlaceSelectAble, addMagicPlaceSelectAble,
clearMagicSelectInfo, clearMagicSelectInfo,
removeHand,
} = duelSlice.actions; } = duelSlice.actions;
export const selectDuelHsStart = (state: RootState) => { export const selectDuelHsStart = (state: RootState) => {
return state.duel.meInitInfo != null; return state.duel.meInitInfo != null;
......
import { judgeSelf, Monster, InteractType } from "./util"; import { judgeSelf, Monster, InteractType } from "./util";
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit"; import {
PayloadAction,
CaseReducer,
createAsyncThunk,
ActionReducerMapBuilder,
} 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 { CardMeta, fetchCard } from "../../api/cards";
export interface MonsterState { export interface MonsterState {
monsters: Monster[]; monsters: Monster[];
...@@ -78,8 +84,76 @@ export const clearMonsterSelectInfoImpl: CaseReducer< ...@@ -78,8 +84,76 @@ export const clearMonsterSelectInfoImpl: CaseReducer<
: state.opMonsters; : state.opMonsters;
if (monsters) { if (monsters) {
monsters.monsters = []; for (const monster of monsters.monsters) {
monster.selectInfo = undefined;
}
}
};
// 增加怪兽
export const fetchMonsterMeta = createAsyncThunk(
"duel/fetchMonsterMeta",
async (param: [number, number, number]) => {
const controler = param[0];
const sequence = param[1];
const code = param[2];
const meta = await fetchCard(code);
const response: [number, number, CardMeta] = [controler, sequence, meta];
return response;
} }
);
export const monsterCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchMonsterMeta.pending, (state, action) => {
// Meta结果没返回之前先更新`ID`
const controler = action.meta.arg[0];
const sequence = action.meta.arg[1];
const code = action.meta.arg[2];
const cardMeta = { id: code, data: {}, text: {} };
if (judgeSelf(controler, state)) {
if (state.meMonsters) {
for (const monster of state.meMonsters.monsters) {
if (monster.sequence == sequence) {
monster.occupant = cardMeta;
}
}
}
} else {
if (state.opMonsters) {
for (const monster of state.opMonsters.monsters) {
if (monster.sequence == sequence) {
monster.occupant = cardMeta;
}
}
}
}
});
builder.addCase(fetchMonsterMeta.fulfilled, (state, action) => {
const controler = action.payload[0];
const sequence = action.payload[1];
const meta = action.payload[2];
if (judgeSelf(controler, state)) {
if (state.meMonsters) {
for (const monster of state.meMonsters.monsters) {
if (monster.sequence == sequence) {
monster.occupant = meta;
}
}
}
} else {
if (state.opMonsters) {
for (const monster of state.opMonsters.monsters) {
if (monster.sequence == sequence) {
monster.occupant = meta;
}
}
}
}
});
}; };
export const selectMeMonsters = (state: RootState) => export const selectMeMonsters = (state: RootState) =>
......
...@@ -25,10 +25,7 @@ export function judgeSelf(player: number, state: Draft<DuelState>): boolean { ...@@ -25,10 +25,7 @@ export function judgeSelf(player: number, state: Draft<DuelState>): boolean {
} }
} }
/* export interface Hand {
* `Neos`中表示卡牌的通用结构
* */
export interface Card {
meta: CardMeta; meta: CardMeta;
transform: CardTransform; transform: CardTransform;
interactivities: Interactivity<number>[]; interactivities: Interactivity<number>[];
......
...@@ -7,6 +7,7 @@ import onMsgNewPhase from "./newPhase"; ...@@ -7,6 +7,7 @@ import onMsgNewPhase from "./newPhase";
import onMsgHint from "./hint"; import onMsgHint from "./hint";
import onMsgSelectIdleCmd from "./selectIdleCmd"; import onMsgSelectIdleCmd from "./selectIdleCmd";
import onMsgSelectPlace from "./selectPlace"; import onMsgSelectPlace from "./selectPlace";
import onMsgMove from "./move";
export default function handleGameMsg(pb: ygopro.YgoStocMsg) { export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch; const dispatch = store.dispatch;
...@@ -62,6 +63,13 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) { ...@@ -62,6 +63,13 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
break; break;
} }
case "move": {
const move = msg.move;
onMsgMove(move, dispatch);
break;
}
default: { default: {
break; break;
} }
......
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import MsgMove = ygopro.StocGameMessage.MsgMove;
import { AppDispatch } from "../../store";
import { fetchMonsterMeta } from "../../reducers/duel/monstersSlice";
import { removeHand } from "../../reducers/duel/mod";
import { fetchMagicMeta } from "../../reducers/duel/magicSlice";
export default (move: MsgMove, dispatch: AppDispatch) => {
const code = move.code;
const from = move.from;
const to = move.to;
// TODO: reason
switch (from.location) {
case ygopro.CardZone.HAND: {
dispatch(removeHand([from.controler, from.sequence]));
break;
}
default: {
console.log(`Unhandled zone type ${from.location}`);
break;
}
}
switch (to.location) {
case ygopro.CardZone.MZONE: {
dispatch(fetchMonsterMeta([to.controler, to.sequence, code]));
break;
}
case ygopro.CardZone.SZONE: {
dispatch(fetchMagicMeta([to.controler, to.sequence, code]));
break;
}
default: {
console.log(`Unhandled zone type ${to.location}`);
break;
}
}
};
...@@ -2,7 +2,7 @@ import * as BABYLON from "@babylonjs/core"; ...@@ -2,7 +2,7 @@ import * as BABYLON from "@babylonjs/core";
import { useAppSelector } from "../../hook"; import { useAppSelector } from "../../hook";
import { selectMeHands } from "../../reducers/duel/handsSlice"; import { selectMeHands } from "../../reducers/duel/handsSlice";
import * as CONFIG from "../../config/ui"; import * as CONFIG from "../../config/ui";
import { Card, InteractType } from "../../reducers/duel/util"; import { Hand, InteractType } from "../../reducers/duel/util";
import { import {
setCardModalImgUrl, setCardModalImgUrl,
setCardModalIsOpen, setCardModalIsOpen,
...@@ -20,13 +20,13 @@ const Hands = () => { ...@@ -20,13 +20,13 @@ const Hands = () => {
return ( return (
<> <>
{hands.map((hand, idx) => { {hands.map((hand, idx) => {
return <Hand state={hand} idx={idx} key={idx} />; return <CHand state={hand} idx={idx} key={idx} />;
})} })}
</> </>
); );
}; };
const Hand = (props: { state: Card; idx: number }) => { const CHand = (props: { state: Hand; idx: number }) => {
const handShape = CONFIG.HandShape(); const handShape = CONFIG.HandShape();
const hoverScale = CONFIG.HandHoverScaling(); const hoverScale = CONFIG.HandHoverScaling();
const defaultScale = new BABYLON.Vector3(1, 1, 1); const defaultScale = new BABYLON.Vector3(1, 1, 1);
......
...@@ -71,7 +71,7 @@ const CMagic = (props: { state: Magic }) => { ...@@ -71,7 +71,7 @@ const CMagic = (props: { state: Magic }) => {
? new BABYLON.Texture(`http://localhost:3030/images/card_back.jpg`) ? new BABYLON.Texture(`http://localhost:3030/images/card_back.jpg`)
: new BABYLON.Texture(`http://localhost:3030/images/card_slot.png`) : new BABYLON.Texture(`http://localhost:3030/images/card_slot.png`)
} }
alpha={0.2} alpha={state.occupant ? 1 : 0}
></standardMaterial> ></standardMaterial>
</plane> </plane>
); );
......
...@@ -72,7 +72,7 @@ const CommonMonster = (props: { state: Monster }) => { ...@@ -72,7 +72,7 @@ const CommonMonster = (props: { state: Monster }) => {
) )
: new BABYLON.Texture(`http://localhost:3030/images/card_slot.png`) : new BABYLON.Texture(`http://localhost:3030/images/card_slot.png`)
} }
alpha={0.2} alpha={props.state.occupant ? 1 : 0}
></standardMaterial> ></standardMaterial>
</plane> </plane>
); );
......
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