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;
export const MSG_HINT = 2;
export const MSG_SELECT_IDLE_CMD = 11;
export const MSG_SELECT_PLACE = 18;
export const MSG_MOVE = 50;
......@@ -13,6 +13,7 @@ import MsgNewPhaseAdapter from "./newPhase";
import MsgHintAdapter from "./hint";
import MsgSelectIdleCmdAdapter from "./selectIdleCmd";
import MsgSelectPlaceAdapter from "./selectPlace";
import MsgMoveAdapter from "./move";
/*
* STOC GameMsg
......@@ -73,6 +74,11 @@ export default class GameMsgAdapter implements StocAdapter {
break;
}
case GAME_MSG.MSG_MOVE: {
gameMsg.move = MsgMoveAdapter(gameData);
break;
}
default: {
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 {
}
}
}
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 {
import { DuelState } from "./mod";
import { RootState } from "../../store";
import { fetchCard, CardMeta } from "../../api/cards";
import { judgeSelf, Card, Interactivity } from "./util";
import { judgeSelf, Hand, Interactivity } from "./util";
import * as UICONFIG from "../../config/ui";
export interface Hands {
// 注意:手牌的位置顺序是有约束的
cards: Card[];
cards: Hand[];
}
// 增加手牌
......@@ -86,7 +86,7 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
// 更新手牌的位置和旋转信息
//
// TODO: 兼容对方手牌
function setHandsTransform(hands: Card[]): void {
function setHandsTransform(hands: Hand[]): void {
const groundShape = UICONFIG.GroundShape();
const handShape = UICONFIG.HandShape();
const gap = groundShape.width / (hands.length - 1);
......@@ -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) =>
state.duel.meHands || { cards: [] };
export const selectOpHands = (state: RootState) =>
......
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 { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { RootState } from "../../store";
import { CardMeta, fetchCard } from "../../api/cards";
export interface MagicState {
magics: Magic[];
......@@ -74,8 +80,76 @@ export const clearMagicSelectInfoImpl: CaseReducer<
const magics = judgeSelf(player, state) ? state.meMagics : state.opMagics;
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) =>
......
......@@ -11,6 +11,7 @@ import {
handsCase,
clearHandsInteractivityImpl,
addHandsInteractivityImpl,
removeHandImpl,
} from "./handsSlice";
import { newTurnImpl } from "./turnSlice";
import { newPhaseImpl } from "./phaseSlice";
......@@ -28,12 +29,14 @@ import {
initMonstersImpl,
addMonsterPlaceSelectAbleImpl,
clearMonsterSelectInfoImpl,
monsterCase,
} from "./monstersSlice";
import {
MagicState,
initMagicsImpl,
addMagicPlaceSelectAbleImpl,
clearMagicSelectInfoImpl,
magicCase,
} from "./magicSlice";
export interface DuelState {
......@@ -84,6 +87,7 @@ const duelSlice = createSlice({
// 手牌相关`Reducer`
clearHandsInteractivity: clearHandsInteractivityImpl,
addHandsInteractivity: addHandsInteractivityImpl,
removeHand: removeHandImpl,
// 怪兽区相关`Reducer`
initMonsters: initMonstersImpl,
......@@ -104,6 +108,8 @@ const duelSlice = createSlice({
extraReducers(builder) {
handsCase(builder);
hintCase(builder);
monsterCase(builder);
magicCase(builder);
},
});
......@@ -125,6 +131,7 @@ export const {
initMagics,
addMagicPlaceSelectAble,
clearMagicSelectInfo,
removeHand,
} = duelSlice.actions;
export const selectDuelHsStart = (state: RootState) => {
return state.duel.meInitInfo != null;
......
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 { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { RootState } from "../../store";
import { CardMeta, fetchCard } from "../../api/cards";
export interface MonsterState {
monsters: Monster[];
......@@ -78,8 +84,76 @@ export const clearMonsterSelectInfoImpl: CaseReducer<
: state.opMonsters;
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) =>
......
......@@ -25,10 +25,7 @@ export function judgeSelf(player: number, state: Draft<DuelState>): boolean {
}
}
/*
* `Neos`中表示卡牌的通用结构
* */
export interface Card {
export interface Hand {
meta: CardMeta;
transform: CardTransform;
interactivities: Interactivity<number>[];
......
......@@ -7,6 +7,7 @@ import onMsgNewPhase from "./newPhase";
import onMsgHint from "./hint";
import onMsgSelectIdleCmd from "./selectIdleCmd";
import onMsgSelectPlace from "./selectPlace";
import onMsgMove from "./move";
export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
......@@ -62,6 +63,13 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
break;
}
case "move": {
const move = msg.move;
onMsgMove(move, dispatch);
break;
}
default: {
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";
import { useAppSelector } from "../../hook";
import { selectMeHands } from "../../reducers/duel/handsSlice";
import * as CONFIG from "../../config/ui";
import { Card, InteractType } from "../../reducers/duel/util";
import { Hand, InteractType } from "../../reducers/duel/util";
import {
setCardModalImgUrl,
setCardModalIsOpen,
......@@ -20,13 +20,13 @@ const Hands = () => {
return (
<>
{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 hoverScale = CONFIG.HandHoverScaling();
const defaultScale = new BABYLON.Vector3(1, 1, 1);
......
......@@ -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_slot.png`)
}
alpha={0.2}
alpha={state.occupant ? 1 : 0}
></standardMaterial>
</plane>
);
......
......@@ -72,7 +72,7 @@ const CommonMonster = (props: { state: Monster }) => {
)
: new BABYLON.Texture(`http://localhost:3030/images/card_slot.png`)
}
alpha={0.2}
alpha={props.state.occupant ? 1 : 0}
></standardMaterial>
</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