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
...@@ -18,6 +18,16 @@ export namespace ygopro { ...@@ -18,6 +18,16 @@ export namespace ygopro {
FZONE = 9, FZONE = 9,
PZONE = 10, PZONE = 10,
} }
export enum CardPosition {
FACEUP_ATTACK = 0,
FACEDOWN_ATTACK = 1,
FACEUP_DEFENSE = 2,
FACEDOWN_DEFENSE = 3,
FACEUP = 4,
FACEDOWN = 5,
ATTACK = 6,
DEFENSE = 7,
}
export enum HandType { export enum HandType {
UNKNOWN = 0, UNKNOWN = 0,
SCISSORS = 1, SCISSORS = 1,
...@@ -170,6 +180,180 @@ export namespace ygopro { ...@@ -170,6 +180,180 @@ export namespace ygopro {
return CardInfo.deserialize(bytes); return CardInfo.deserialize(bytes);
} }
} }
export class CardLocation extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(
data?:
| any[]
| {
controler?: number;
location?: number;
sequence?: number;
position?: CardPosition;
overlay_sequence?: number;
}
) {
super();
pb_1.Message.initialize(
this,
Array.isArray(data) ? data : [],
0,
-1,
[],
this.#one_of_decls
);
if (!Array.isArray(data) && typeof data == "object") {
if ("controler" in data && data.controler != undefined) {
this.controler = data.controler;
}
if ("location" in data && data.location != undefined) {
this.location = data.location;
}
if ("sequence" in data && data.sequence != undefined) {
this.sequence = data.sequence;
}
if ("position" in data && data.position != undefined) {
this.position = data.position;
}
if ("overlay_sequence" in data && data.overlay_sequence != undefined) {
this.overlay_sequence = data.overlay_sequence;
}
}
}
get controler() {
return pb_1.Message.getFieldWithDefault(this, 1, 0) as number;
}
set controler(value: number) {
pb_1.Message.setField(this, 1, value);
}
get location() {
return pb_1.Message.getFieldWithDefault(this, 2, 0) as number;
}
set location(value: number) {
pb_1.Message.setField(this, 2, value);
}
get sequence() {
return pb_1.Message.getFieldWithDefault(this, 3, 0) as number;
}
set sequence(value: number) {
pb_1.Message.setField(this, 3, value);
}
get position() {
return pb_1.Message.getFieldWithDefault(
this,
4,
CardPosition.FACEUP_ATTACK
) as CardPosition;
}
set position(value: CardPosition) {
pb_1.Message.setField(this, 4, value);
}
get overlay_sequence() {
return pb_1.Message.getFieldWithDefault(this, 5, 0) as number;
}
set overlay_sequence(value: number) {
pb_1.Message.setField(this, 5, value);
}
static fromObject(data: {
controler?: number;
location?: number;
sequence?: number;
position?: CardPosition;
overlay_sequence?: number;
}): CardLocation {
const message = new CardLocation({});
if (data.controler != null) {
message.controler = data.controler;
}
if (data.location != null) {
message.location = data.location;
}
if (data.sequence != null) {
message.sequence = data.sequence;
}
if (data.position != null) {
message.position = data.position;
}
if (data.overlay_sequence != null) {
message.overlay_sequence = data.overlay_sequence;
}
return message;
}
toObject() {
const data: {
controler?: number;
location?: number;
sequence?: number;
position?: CardPosition;
overlay_sequence?: number;
} = {};
if (this.controler != null) {
data.controler = this.controler;
}
if (this.location != null) {
data.location = this.location;
}
if (this.sequence != null) {
data.sequence = this.sequence;
}
if (this.position != null) {
data.position = this.position;
}
if (this.overlay_sequence != null) {
data.overlay_sequence = this.overlay_sequence;
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.controler != 0) writer.writeInt32(1, this.controler);
if (this.location != 0) writer.writeInt32(2, this.location);
if (this.sequence != 0) writer.writeInt32(3, this.sequence);
if (this.position != CardPosition.FACEUP_ATTACK)
writer.writeEnum(4, this.position);
if (this.overlay_sequence != 0)
writer.writeInt32(5, this.overlay_sequence);
if (!w) return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): CardLocation {
const reader =
bytes instanceof pb_1.BinaryReader
? bytes
: new pb_1.BinaryReader(bytes),
message = new CardLocation();
while (reader.nextField()) {
if (reader.isEndGroup()) break;
switch (reader.getFieldNumber()) {
case 1:
message.controler = reader.readInt32();
break;
case 2:
message.location = reader.readInt32();
break;
case 3:
message.sequence = reader.readInt32();
break;
case 4:
message.position = reader.readEnum();
break;
case 5:
message.overlay_sequence = reader.readInt32();
break;
default:
reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): CardLocation {
return CardLocation.deserialize(bytes);
}
}
export class YgoCtosMsg extends pb_1.Message { export class YgoCtosMsg extends pb_1.Message {
#one_of_decls: number[][] = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]; #one_of_decls: number[][] = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]];
constructor( constructor(
...@@ -3979,7 +4163,7 @@ export namespace ygopro { ...@@ -3979,7 +4163,7 @@ export namespace ygopro {
} }
} }
export class StocGameMessage extends pb_1.Message { export class StocGameMessage extends pb_1.Message {
#one_of_decls: number[][] = [[1, 2, 3, 4, 5, 6, 7]]; #one_of_decls: number[][] = [[1, 2, 3, 4, 5, 6, 7, 8]];
constructor( constructor(
data?: data?:
| any[] | any[]
...@@ -3992,6 +4176,7 @@ export namespace ygopro { ...@@ -3992,6 +4176,7 @@ export namespace ygopro {
hint?: never; hint?: never;
select_idle_cmd?: never; select_idle_cmd?: never;
select_place?: never; select_place?: never;
move?: never;
} }
| { | {
start?: never; start?: never;
...@@ -4001,6 +4186,7 @@ export namespace ygopro { ...@@ -4001,6 +4186,7 @@ export namespace ygopro {
hint?: never; hint?: never;
select_idle_cmd?: never; select_idle_cmd?: never;
select_place?: never; select_place?: never;
move?: never;
} }
| { | {
start?: never; start?: never;
...@@ -4010,6 +4196,7 @@ export namespace ygopro { ...@@ -4010,6 +4196,7 @@ export namespace ygopro {
hint?: never; hint?: never;
select_idle_cmd?: never; select_idle_cmd?: never;
select_place?: never; select_place?: never;
move?: never;
} }
| { | {
start?: never; start?: never;
...@@ -4019,6 +4206,7 @@ export namespace ygopro { ...@@ -4019,6 +4206,7 @@ export namespace ygopro {
hint?: never; hint?: never;
select_idle_cmd?: never; select_idle_cmd?: never;
select_place?: never; select_place?: never;
move?: never;
} }
| { | {
start?: never; start?: never;
...@@ -4028,6 +4216,7 @@ export namespace ygopro { ...@@ -4028,6 +4216,7 @@ export namespace ygopro {
hint?: StocGameMessage.MsgHint; hint?: StocGameMessage.MsgHint;
select_idle_cmd?: never; select_idle_cmd?: never;
select_place?: never; select_place?: never;
move?: never;
} }
| { | {
start?: never; start?: never;
...@@ -4037,6 +4226,7 @@ export namespace ygopro { ...@@ -4037,6 +4226,7 @@ export namespace ygopro {
hint?: never; hint?: never;
select_idle_cmd?: StocGameMessage.MsgSelectIdleCmd; select_idle_cmd?: StocGameMessage.MsgSelectIdleCmd;
select_place?: never; select_place?: never;
move?: never;
} }
| { | {
start?: never; start?: never;
...@@ -4046,6 +4236,17 @@ export namespace ygopro { ...@@ -4046,6 +4236,17 @@ export namespace ygopro {
hint?: never; hint?: never;
select_idle_cmd?: never; select_idle_cmd?: never;
select_place?: StocGameMessage.MsgSelectPlace; select_place?: StocGameMessage.MsgSelectPlace;
move?: never;
}
| {
start?: never;
draw?: never;
new_turn?: never;
new_phase?: never;
hint?: never;
select_idle_cmd?: never;
select_place?: never;
move?: StocGameMessage.MsgMove;
} }
)) ))
) { ) {
...@@ -4080,6 +4281,9 @@ export namespace ygopro { ...@@ -4080,6 +4281,9 @@ export namespace ygopro {
if ("select_place" in data && data.select_place != undefined) { if ("select_place" in data && data.select_place != undefined) {
this.select_place = data.select_place; this.select_place = data.select_place;
} }
if ("move" in data && data.move != undefined) {
this.move = data.move;
}
} }
} }
get start() { get start() {
...@@ -4173,6 +4377,19 @@ export namespace ygopro { ...@@ -4173,6 +4377,19 @@ export namespace ygopro {
get has_select_place() { get has_select_place() {
return pb_1.Message.getField(this, 7) != null; return pb_1.Message.getField(this, 7) != null;
} }
get move() {
return pb_1.Message.getWrapperField(
this,
StocGameMessage.MsgMove,
8
) as StocGameMessage.MsgMove;
}
set move(value: StocGameMessage.MsgMove) {
pb_1.Message.setOneofWrapperField(this, 8, this.#one_of_decls[0], value);
}
get has_move() {
return pb_1.Message.getField(this, 8) != null;
}
get gameMsg() { get gameMsg() {
const cases: { const cases: {
[index: number]: [index: number]:
...@@ -4183,7 +4400,8 @@ export namespace ygopro { ...@@ -4183,7 +4400,8 @@ export namespace ygopro {
| "new_phase" | "new_phase"
| "hint" | "hint"
| "select_idle_cmd" | "select_idle_cmd"
| "select_place"; | "select_place"
| "move";
} = { } = {
0: "none", 0: "none",
1: "start", 1: "start",
...@@ -4193,8 +4411,11 @@ export namespace ygopro { ...@@ -4193,8 +4411,11 @@ export namespace ygopro {
5: "hint", 5: "hint",
6: "select_idle_cmd", 6: "select_idle_cmd",
7: "select_place", 7: "select_place",
8: "move",
}; };
return cases[pb_1.Message.computeOneofCase(this, [1, 2, 3, 4, 5, 6, 7])]; return cases[
pb_1.Message.computeOneofCase(this, [1, 2, 3, 4, 5, 6, 7, 8])
];
} }
static fromObject(data: { static fromObject(data: {
start?: ReturnType<typeof StocGameMessage.MsgStart.prototype.toObject>; start?: ReturnType<typeof StocGameMessage.MsgStart.prototype.toObject>;
...@@ -4212,6 +4433,7 @@ export namespace ygopro { ...@@ -4212,6 +4433,7 @@ export namespace ygopro {
select_place?: ReturnType< select_place?: ReturnType<
typeof StocGameMessage.MsgSelectPlace.prototype.toObject typeof StocGameMessage.MsgSelectPlace.prototype.toObject
>; >;
move?: ReturnType<typeof StocGameMessage.MsgMove.prototype.toObject>;
}): StocGameMessage { }): StocGameMessage {
const message = new StocGameMessage({}); const message = new StocGameMessage({});
if (data.start != null) { if (data.start != null) {
...@@ -4241,6 +4463,9 @@ export namespace ygopro { ...@@ -4241,6 +4463,9 @@ export namespace ygopro {
data.select_place data.select_place
); );
} }
if (data.move != null) {
message.move = StocGameMessage.MsgMove.fromObject(data.move);
}
return message; return message;
} }
toObject() { toObject() {
...@@ -4260,6 +4485,7 @@ export namespace ygopro { ...@@ -4260,6 +4485,7 @@ export namespace ygopro {
select_place?: ReturnType< select_place?: ReturnType<
typeof StocGameMessage.MsgSelectPlace.prototype.toObject typeof StocGameMessage.MsgSelectPlace.prototype.toObject
>; >;
move?: ReturnType<typeof StocGameMessage.MsgMove.prototype.toObject>;
} = {}; } = {};
if (this.start != null) { if (this.start != null) {
data.start = this.start.toObject(); data.start = this.start.toObject();
...@@ -4282,6 +4508,9 @@ export namespace ygopro { ...@@ -4282,6 +4508,9 @@ export namespace ygopro {
if (this.select_place != null) { if (this.select_place != null) {
data.select_place = this.select_place.toObject(); data.select_place = this.select_place.toObject();
} }
if (this.move != null) {
data.move = this.move.toObject();
}
return data; return data;
} }
serialize(): Uint8Array; serialize(): Uint8Array;
...@@ -4310,6 +4539,8 @@ export namespace ygopro { ...@@ -4310,6 +4539,8 @@ export namespace ygopro {
writer.writeMessage(7, this.select_place, () => writer.writeMessage(7, this.select_place, () =>
this.select_place.serialize(writer) this.select_place.serialize(writer)
); );
if (this.has_move)
writer.writeMessage(8, this.move, () => this.move.serialize(writer));
if (!w) return writer.getResultBuffer(); if (!w) return writer.getResultBuffer();
} }
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): StocGameMessage { static deserialize(bytes: Uint8Array | pb_1.BinaryReader): StocGameMessage {
...@@ -4372,6 +4603,12 @@ export namespace ygopro { ...@@ -4372,6 +4603,12 @@ export namespace ygopro {
StocGameMessage.MsgSelectPlace.deserialize(reader)) StocGameMessage.MsgSelectPlace.deserialize(reader))
); );
break; break;
case 8:
reader.readMessage(
message.move,
() => (message.move = StocGameMessage.MsgMove.deserialize(reader))
);
break;
default: default:
reader.skipField(); reader.skipField();
} }
...@@ -5835,5 +6072,173 @@ export namespace ygopro { ...@@ -5835,5 +6072,173 @@ export namespace ygopro {
} }
} }
} }
export class MsgMove extends pb_1.Message {
#one_of_decls: number[][] = [];
constructor(
data?:
| any[]
| {
code?: number;
from?: CardLocation;
to?: CardLocation;
reason?: number;
}
) {
super();
pb_1.Message.initialize(
this,
Array.isArray(data) ? data : [],
0,
-1,
[],
this.#one_of_decls
);
if (!Array.isArray(data) && typeof data == "object") {
if ("code" in data && data.code != undefined) {
this.code = data.code;
}
if ("from" in data && data.from != undefined) {
this.from = data.from;
}
if ("to" in data && data.to != undefined) {
this.to = data.to;
}
if ("reason" in data && data.reason != undefined) {
this.reason = data.reason;
}
}
}
get code() {
return pb_1.Message.getFieldWithDefault(this, 1, 0) as number;
}
set code(value: number) {
pb_1.Message.setField(this, 1, value);
}
get from() {
return pb_1.Message.getWrapperField(
this,
CardLocation,
2
) as CardLocation;
}
set from(value: CardLocation) {
pb_1.Message.setWrapperField(this, 2, value);
}
get has_from() {
return pb_1.Message.getField(this, 2) != null;
}
get to() {
return pb_1.Message.getWrapperField(
this,
CardLocation,
3
) as CardLocation;
}
set to(value: CardLocation) {
pb_1.Message.setWrapperField(this, 3, value);
}
get has_to() {
return pb_1.Message.getField(this, 3) != null;
}
get reason() {
return pb_1.Message.getFieldWithDefault(this, 4, 0) as number;
}
set reason(value: number) {
pb_1.Message.setField(this, 4, value);
}
static fromObject(data: {
code?: number;
from?: ReturnType<typeof CardLocation.prototype.toObject>;
to?: ReturnType<typeof CardLocation.prototype.toObject>;
reason?: number;
}): MsgMove {
const message = new MsgMove({});
if (data.code != null) {
message.code = data.code;
}
if (data.from != null) {
message.from = CardLocation.fromObject(data.from);
}
if (data.to != null) {
message.to = CardLocation.fromObject(data.to);
}
if (data.reason != null) {
message.reason = data.reason;
}
return message;
}
toObject() {
const data: {
code?: number;
from?: ReturnType<typeof CardLocation.prototype.toObject>;
to?: ReturnType<typeof CardLocation.prototype.toObject>;
reason?: number;
} = {};
if (this.code != null) {
data.code = this.code;
}
if (this.from != null) {
data.from = this.from.toObject();
}
if (this.to != null) {
data.to = this.to.toObject();
}
if (this.reason != null) {
data.reason = this.reason;
}
return data;
}
serialize(): Uint8Array;
serialize(w: pb_1.BinaryWriter): void;
serialize(w?: pb_1.BinaryWriter): Uint8Array | void {
const writer = w || new pb_1.BinaryWriter();
if (this.code != 0) writer.writeInt32(1, this.code);
if (this.has_from)
writer.writeMessage(2, this.from, () => this.from.serialize(writer));
if (this.has_to)
writer.writeMessage(3, this.to, () => this.to.serialize(writer));
if (this.reason != 0) writer.writeInt32(4, this.reason);
if (!w) return writer.getResultBuffer();
}
static deserialize(bytes: Uint8Array | pb_1.BinaryReader): MsgMove {
const reader =
bytes instanceof pb_1.BinaryReader
? bytes
: new pb_1.BinaryReader(bytes),
message = new MsgMove();
while (reader.nextField()) {
if (reader.isEndGroup()) break;
switch (reader.getFieldNumber()) {
case 1:
message.code = reader.readInt32();
break;
case 2:
reader.readMessage(
message.from,
() => (message.from = CardLocation.deserialize(reader))
);
break;
case 3:
reader.readMessage(
message.to,
() => (message.to = CardLocation.deserialize(reader))
);
break;
case 4:
message.reason = reader.readInt32();
break;
default:
reader.skipField();
}
}
return message;
}
serializeBinary(): Uint8Array {
return this.serialize();
}
static deserializeBinary(bytes: Uint8Array): MsgMove {
return MsgMove.deserialize(bytes);
}
}
} }
} }
...@@ -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