Commit e6e7bab4 authored by Chunchi Che's avatar Chunchi Che

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

Feat/service/shuffle hand

See merge request !76
parents ddd5f1ac c7f3bbcf
Pipeline #19528 passed with stages
in 5 minutes and 15 seconds
neos-protobuf @ 9b1e1b0f
Subproject commit 707b028ef6ecbe6de328f9e1114707553e0f3df2 Subproject commit 9b1e1b0fdb8982b80c535178f44981cb17206866
This diff is collapsed.
...@@ -19,5 +19,19 @@ ...@@ -19,5 +19,19 @@
"fieldType": "uint8" "fieldType": "uint8"
} }
] ]
},
"33": {
"protoType": "shuffle_hand",
"fields": [
{
"fieldName": "player",
"fieldType": "uint8"
},
{
"fieldName": "hands",
"fieldType": "repeated",
"repeatedType": "uint32"
}
]
} }
} }
...@@ -13,7 +13,8 @@ const ReadFieldHandlerMap: Map<string, readFieldHandler> = new Map([ ...@@ -13,7 +13,8 @@ const ReadFieldHandlerMap: Map<string, readFieldHandler> = new Map([
["CardLocation", (reader) => reader.readCardLocation()], ["CardLocation", (reader) => reader.readCardLocation()],
]); ]);
const MsgConstructorMap: Map<string, Constructor> = new Map([ const MsgConstructorMap: Map<string, Constructor> = new Map([
["move", ygopro.StocGameMessage.MsgMove], ["move", ygopro.StocGameMessage.MsgMove as Constructor],
["shuffle_hand", ygopro.StocGameMessage.MsgShuffleHand],
]); ]);
export interface penetrateType { export interface penetrateType {
...@@ -21,6 +22,7 @@ export interface penetrateType { ...@@ -21,6 +22,7 @@ export interface penetrateType {
fields: { fields: {
fieldName: string; fieldName: string;
fieldType: string; fieldType: string;
repeatedType?: string;
}[]; }[];
} }
...@@ -46,6 +48,22 @@ class PenetrateManager { ...@@ -46,6 +48,22 @@ class PenetrateManager {
return undefined; return undefined;
} }
private readRepeatedField(reader: BufferReader, repeatedType: string): any {
const handler = this.readFieldHandlerMap.get(repeatedType);
if (handler) {
const count = reader.readUint8();
let repeated = [];
for (let i = 0; i < count; i++) {
repeated.push(handler(reader));
}
return repeated;
}
return undefined;
}
private constructMsg(protoType: string, object: any): any { private constructMsg(protoType: string, object: any): any {
const constructor = this.msgConstructorMap.get(protoType); const constructor = this.msgConstructorMap.get(protoType);
...@@ -65,7 +83,10 @@ class PenetrateManager { ...@@ -65,7 +83,10 @@ class PenetrateManager {
let object: any = {}; let object: any = {};
for (let field of fields) { for (let field of fields) {
object[field.fieldName] = this.readField(reader, field.fieldType); object[field.fieldName] =
field.fieldType === "repeated" && field.repeatedType
? this.readRepeatedField(reader, field.repeatedType)
: this.readField(reader, field.fieldType);
} }
gameMsg[protoType] = this.constructMsg(protoType, object); gameMsg[protoType] = this.constructMsg(protoType, object);
......
...@@ -80,6 +80,35 @@ export function createAsyncMetaThunk(name: string): AsyncThunk< ...@@ -80,6 +80,35 @@ export function createAsyncMetaThunk(name: string): AsyncThunk<
); );
} }
export function createAsyncRepeatedMetaThunk(
name: string
): AsyncThunk<
{ controler: number; metas: CardMeta[] },
{ controler: number; codes: number[] },
{}
> {
return createAsyncThunk(
name,
async (param: { controler: number; codes: number[] }) => {
const controler = param.controler;
const Ids = param.codes;
const metas = await Promise.all(
Ids.map(async (id) => {
if (id == 0) {
return { id, data: {}, text: {} };
} else {
return await fetchCard(id);
}
})
);
const response = { controler, metas };
return response;
}
);
}
export function extendState<T extends DuelFieldState>( export function extendState<T extends DuelFieldState>(
state: T | undefined, state: T | undefined,
newState: CardState newState: CardState
...@@ -180,3 +209,18 @@ export function insertCard<T extends DuelFieldState>( ...@@ -180,3 +209,18 @@ export function insertCard<T extends DuelFieldState>(
state.inner.splice(sequence, 0, card); state.inner.splice(sequence, 0, card);
} }
} }
export function updateCardMeta<T extends DuelFieldState>(
state: T | undefined,
metas: CardMeta[]
) {
if (state) {
state.inner.forEach((item) => {
metas.forEach((meta) => {
if (item.occupant?.id === meta.id) {
item.occupant = meta;
}
});
});
}
}
import { import {
createAsyncThunk,
ActionReducerMapBuilder, ActionReducerMapBuilder,
CaseReducer, CaseReducer,
PayloadAction, PayloadAction,
} from "@reduxjs/toolkit"; } from "@reduxjs/toolkit";
import { DuelState } from "./mod"; import { DuelState } from "./mod";
import { RootState } from "../../store"; import { RootState } from "../../store";
import { fetchCard, CardMeta } from "../../api/cards";
import { judgeSelf } from "./util"; import { judgeSelf } from "./util";
import { import {
Interactivity, Interactivity,
...@@ -15,31 +13,16 @@ import { ...@@ -15,31 +13,16 @@ import {
createAsyncMetaThunk, createAsyncMetaThunk,
insertCard, insertCard,
extendMeta, extendMeta,
createAsyncRepeatedMetaThunk,
updateCardMeta,
} from "./generic"; } from "./generic";
import { ygopro } from "../../api/ocgcore/idl/ocgcore"; import { ygopro } from "../../api/ocgcore/idl/ocgcore";
export interface HandState extends DuelFieldState {} export interface HandState extends DuelFieldState {}
// 增加手牌 // 增加手牌
export const fetchHandsMeta = createAsyncThunk( export const fetchHandsMeta = createAsyncRepeatedMetaThunk(
"duel/fetchHandsMeta", "duel/fetchHandsMeta"
async (param: [number, number[]]) => {
const player = param[0];
const Ids = param[1];
const metas = await Promise.all(
Ids.map(async (id) => {
if (id === 0) {
return { id, data: {}, text: {} };
} else {
return await fetchCard(id);
}
})
);
const response: [number, CardMeta[]] = [player, metas];
return response;
}
); );
// 清空手牌互动性 // 清空手牌互动性
...@@ -92,19 +75,22 @@ export const removeHandImpl: CaseReducer< ...@@ -92,19 +75,22 @@ export const removeHandImpl: CaseReducer<
export const insertHandMeta = createAsyncMetaThunk("duel/insertHandMeta"); export const insertHandMeta = createAsyncMetaThunk("duel/insertHandMeta");
export const updateHandsMeta = createAsyncRepeatedMetaThunk(
"duel/updateHandsMeta"
);
export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => { export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchHandsMeta.pending, (state, action) => { builder.addCase(fetchHandsMeta.pending, (state, action) => {
// Meta结果没返回之前先更新手牌`ID` // Meta结果没返回之前先更新手牌`ID`
const player = action.meta.arg[0]; const player = action.meta.arg.controler;
const ids = action.meta.arg[1]; const ids = action.meta.arg.codes;
const cards = ids.map((id, idx) => { const cards = ids.map((id) => {
return { return {
occupant: { id, data: {}, text: {} }, occupant: { id, data: {}, text: {} },
location: { location: {
controler: player, controler: player,
location: ygopro.CardZone.HAND, location: ygopro.CardZone.HAND,
sequence: idx,
}, },
idleInteractivities: [], idleInteractivities: [],
}; };
...@@ -125,19 +111,11 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -125,19 +111,11 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
}); });
builder.addCase(fetchHandsMeta.fulfilled, (state, action) => { builder.addCase(fetchHandsMeta.fulfilled, (state, action) => {
// `Meta`结果回来后更新手牌的`Meta`结果 // `Meta`结果回来后更新手牌的`Meta`结果
const player = action.payload[0]; const player = action.payload.controler;
const metas = action.payload[1]; const metas = action.payload.metas;
const hands = judgeSelf(player, state) ? state.meHands : state.opHands; const hands = judgeSelf(player, state) ? state.meHands : state.opHands;
if (hands) { updateCardMeta(hands, metas);
for (let hand of hands.inner) {
for (let meta of metas) {
if (hand.occupant?.id === meta.id) {
hand.occupant = meta;
}
}
}
}
}); });
builder.addCase(insertHandMeta.pending, (state, action) => { builder.addCase(insertHandMeta.pending, (state, action) => {
...@@ -162,6 +140,35 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => { ...@@ -162,6 +140,35 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
extendMeta(hands, meta, sequence); extendMeta(hands, meta, sequence);
}); });
builder.addCase(updateHandsMeta.pending, (state, action) => {
const controler = action.meta.arg.controler;
const codes = action.meta.arg.codes;
const metas = codes.map((code) => {
return {
occupant: { id: code, data: {}, text: {} },
location: {
controler,
location: ygopro.CardZone.HAND,
},
idleInteractivities: [],
};
});
const hands = judgeSelf(controler, state) ? state.meHands : state.opHands;
if (hands) {
hands.inner = metas;
}
});
builder.addCase(updateHandsMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const metas = action.payload.metas;
const hands = judgeSelf(controler, state) ? state.meHands : state.opHands;
updateCardMeta(hands, metas);
});
}; };
// 在特定位置增加手牌 // 在特定位置增加手牌
......
...@@ -6,5 +6,5 @@ export default ( ...@@ -6,5 +6,5 @@ export default (
draw: ygopro.StocGameMessage.MsgDraw, draw: ygopro.StocGameMessage.MsgDraw,
dispatch: AppDispatch dispatch: AppDispatch
) => { ) => {
dispatch(fetchHandsMeta([draw.player, draw.cards])); dispatch(fetchHandsMeta({ controler: draw.player, codes: draw.cards }));
}; };
...@@ -13,6 +13,7 @@ import onMsgSelectChain from "./selectChain"; ...@@ -13,6 +13,7 @@ import onMsgSelectChain from "./selectChain";
import onMsgSelectEffectYn from "./selectEffectYn"; import onMsgSelectEffectYn from "./selectEffectYn";
import onMsgSelectPosition from "./selectPosition"; import onMsgSelectPosition from "./selectPosition";
import onMsgSelectOption from "./selectOption"; import onMsgSelectOption from "./selectOption";
import onMsgShuffleHand from "./shuffleHand";
export default function handleGameMsg(pb: ygopro.YgoStocMsg) { export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch; const dispatch = store.dispatch;
...@@ -97,6 +98,12 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) { ...@@ -97,6 +98,12 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
break; break;
} }
case "shuffle_hand": {
const shuffleHand = msg.shuffle_hand;
onMsgShuffleHand(shuffleHand, dispatch);
break;
}
default: { default: {
break; break;
} }
......
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { updateHandsMeta } from "../../reducers/duel/handsSlice";
import { AppDispatch } from "../../store";
import MsgShuffleHand = ygopro.StocGameMessage.MsgShuffleHand;
export default (shuffleHand: MsgShuffleHand, dispatch: AppDispatch) => {
dispatch(
updateHandsMeta({ controler: shuffleHand.player, codes: shuffleHand.hands })
);
};
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