Commit 0756e4c7 authored by Chunchi Che's avatar Chunchi Che

Merge branch 'dev/refactor-duel' into 'main'

Dev/refactor duel

See merge request !168
parents bc14d8f1 6c4f377c
......@@ -6,7 +6,8 @@
"plugin:import/typescript"
],
"plugins": [
"@typescript-eslint"
"@typescript-eslint",
"simple-import-sort"
],
"rules": {
"eqeqeq": 0,
......@@ -14,7 +15,9 @@
"@typescript-eslint/consistent-type-definitions": [
"error",
"interface"
]
],
"simple-import-sort/imports": "warn",
"simple-import-sort/exports": "warn"
},
"settings": {
"import/resolver": {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
import axios from "axios";
import sqliteMiddleWare, { sqliteCmd } from "@/middleware/sqlite";
export interface CardMeta {
......
import { YgoProPacket } from "./packet";
import { ygopro } from "../idl/ocgcore";
import { YgoProPacket } from "./packet";
import {
STOC_CHAT,
STOC_DECK_COUNT,
STOC_DUEL_START,
STOC_GAME_MSG,
STOC_HAND_RESULT,
STOC_HS_PLAYER_CHANGE,
STOC_HS_PLAYER_ENTER,
STOC_HS_WATCH_CHANGE,
STOC_JOIN_GAME,
STOC_TYPE_CHANGE,
STOC_SELECT_HAND,
STOC_SELECT_TP,
STOC_HAND_RESULT,
STOC_DECK_COUNT,
STOC_DUEL_START,
STOC_GAME_MSG,
STOC_TIME_LIMIT,
STOC_TYPE_CHANGE,
} from "./protoDecl";
import StocChat from "./stoc/stocChat";
import StocJoinGame from "./stoc/stocJoinGame";
import StocHsPlayerEnter from "./stoc/stocHsPlayerEnter";
import StocDeckCount from "./stoc/stocDeckCount";
import StocDuelStart from "./stoc/stocDuelStart";
import StocGameMsg from "./stoc/stocGameMsg/mod";
import StocHsPlayerChange from "./stoc/stocHsPlayerChange";
import StocHsPlayerEnter from "./stoc/stocHsPlayerEnter";
import StocHsWatchChange from "./stoc/stocHsWatchChange";
import StocTypeChange from "./stoc/stocTypeChange";
import StocJoinGame from "./stoc/stocJoinGame";
import StocSelectHand from "./stoc/stocSelectHand";
import StocSelectTp from "./stoc/stocSelectTp";
import StocDeckCount from "./stoc/stocDeckCount";
import StocTimeLimit from "./stoc/stocTimeLimit";
import StocGameMsg from "./stoc/stocGameMsg/mod";
import StocDuelStart from "./stoc/stocDuelStart";
import StocTypeChange from "./stoc/stocTypeChange";
/*
* 将[`ygoProPacket`]对象转换成[`ygopro.YgoStocMsg`]对象
......
import { BufferReader } from "rust-src";
import { ygopro } from "../idl/ocgcore";
import { numberToCardPosition, numberToCardZone } from "./util";
import { BufferReader } from "rust-src";
const LOCATION_OVERLAY = 0x80;
......
import { ygopro } from "../../../idl/ocgcore";
import { YgoProPacket } from "../../packet";
import { CTOS_RESPONSE } from "../../protoDecl";
import adaptSelectIdleCmdResponse from "./selectIdleCmd";
import adaptSelectPlaceResponse from "./selectPlace";
import adaptSelectBattleCmdResponse from "./selectBattleCmd";
import adaptSelectCardResponse from "./selectCard";
import adaptSelectChainResponse from "./selectChain";
import adaptSelectCounterResponse from "./selectCounter";
import adaptSelectEffectYnResponse from "./selectEffectYn";
import adaptSelectPositionResponse from "./selectPosition";
import adaptSelectIdleCmdResponse from "./selectIdleCmd";
import adaptSelectOptionResponse from "./selectOption";
import adaptSelectBattleCmdResponse from "./selectBattleCmd";
import adaptSelectPlaceResponse from "./selectPlace";
import adaptSelectPositionResponse from "./selectPosition";
import adaptSelectUnselectCardResponse from "./selectUnselectCard";
import adaptSelectCounterResponse from "./selectCounter";
import adaptSortCardResponse from "./sortCard";
/*
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
export default (
response: ygopro.CtosGameMsgResponse.SelectBattleCmdResponse
) => {
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
export default (response: ygopro.CtosGameMsgResponse.SelectCardResponse) => {
const writer = new BufferWriter();
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
export default (response: ygopro.CtosGameMsgResponse.SelectChainResponse) => {
const writer = new BufferWriter();
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
export default (response: ygopro.CtosGameMsgResponse.SelectCounterResponse) => {
const writer = new BufferWriter();
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
export default (
response: ygopro.CtosGameMsgResponse.SelectEffectYnResponse
) => {
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
export default (response: ygopro.CtosGameMsgResponse.SelectIdleCmdResponse) => {
const writer = new BufferWriter();
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
export default (response: ygopro.CtosGameMsgResponse.SelectOptionResponse) => {
const writer = new BufferWriter();
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
import { cardZoneToNumber } from "../../util";
export default (response: ygopro.CtosGameMsgResponse.SelectPlaceResponse) => {
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
export default (
response: ygopro.CtosGameMsgResponse.SelectPositionResponse
) => {
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
export default (
response: ygopro.CtosGameMsgResponse.SelectUnselectCardResponse
) => {
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferWriter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
export default (response: ygopro.CtosGameMsgResponse.SortCardResponse) => {
const writer = new BufferWriter();
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { StocAdapter, YgoProPacket } from "../packet";
/*
* STOC Chat
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { StocAdapter, YgoProPacket } from "../packet";
const LITTLE_ENDIAN = true;
const INT16_BYTE_OFFSET = 2;
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { StocAdapter, YgoProPacket } from "../packet";
/*
* STOC DuelStart
......
import { ygopro } from "../../../idl/ocgcore";
import { ocgDamageAdapter } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
/*
* Msg Damage
*
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReader } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
/*
* MSG Draw
*
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReader } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
/*
* Msg Hint
*
......
......@@ -4,35 +4,35 @@
* */
import { ygopro } from "../../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../../packet";
import { StocAdapter, YgoProPacket } from "../../packet";
import * as GAME_MSG from "../../protoDecl";
import MsgStartAdapter from "./start";
import MsgAddCounter from "./addCounter";
import MsgDamage from "./damage";
import MsgDrawAdapter from "./draw";
import MsgNewTurnAdapter from "./newTurn";
import MsgNewPhaseAdapter from "./newPhase";
import MsgHintAdapter from "./hint";
import MsgSelectIdleCmdAdapter from "./selectIdleCmd";
import MsgSelectPlaceAdapter from "./selectPlace";
import MsgNewPhaseAdapter from "./newPhase";
import MsgNewTurnAdapter from "./newTurn";
import PENETRATE from "./penetrate";
import MsgRecover from "./recover";
import MsgReloadFieldAdapter from "./reloadField";
import MsgRemoveCounter from "./removeCounter";
import MsgSelectBattleCmdAdapter from "./selectBattleCmd";
import MsgSelectCardAdapter from "./selectCard";
import MsgSelectTributeAdapter from "./selectTribute";
import MsgSelectChainAdapter from "./selectChain";
import MsgSelectCounter from "./selectCounter";
import MsgSelectEffectYnAdapter from "./selectEffectYn";
import MsgSelectPositionAdapter from "./selectPosition";
import MsgSelectIdleCmdAdapter from "./selectIdleCmd";
import MsgSelectOptionAdapter from "./selectOption";
import MsgSelectBattleCmdAdapter from "./selectBattleCmd";
import MsgSelectPlaceAdapter from "./selectPlace";
import MsgSelectPositionAdapter from "./selectPosition";
import MsgSelectSum from "./selectSum";
import MsgSelectTributeAdapter from "./selectTribute";
import MsgSelectUnselectCardAdapter from "./selectUnselectCard";
import MsgSortCard from "./sortCard";
import MsgStartAdapter from "./start";
import MsgUpdateDataAdapter from "./updateData";
import MsgWaitAdapter from "./wait";
import MsgDamage from "./damage";
import MsgRecover from "./recover";
import MsgWin from "./win";
import MsgUpdateDataAdapter from "./updateData";
import MsgReloadFieldAdapter from "./reloadField";
import MsgSelectSum from "./selectSum";
import MsgAddCounter from "./addCounter";
import MsgRemoveCounter from "./removeCounter";
import MsgSelectCounter from "./selectCounter";
import MsgSortCard from "./sortCard";
import PENETRATE from "./penetrate";
/*
* STOC GameMsg
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReader } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
/*
* Msg New Phase
*
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReader } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
/*
* MSG New Turn
*
......
//! 透传协议
import PenetrateData from "./penetrate.json";
import { BufferReaderExt } from "../../bufferIO";
import { ygopro } from "../../../idl/ocgcore";
import { BufferReaderExt } from "../../bufferIO";
import { numberToCardPosition } from "../../util";
import PenetrateData from "./penetrate.json";
type Constructor<T = any> = new (...args: any[]) => T;
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReader } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
/*
* Msg Recover
*
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReader } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
import MsgSelectOption = ygopro.StocGameMessage.MsgSelectOption;
/*
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReader } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
import MsgSelectPlace = ygopro.StocGameMessage.MsgSelectPlace;
/*
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReader } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
import MsgSelectPosition = ygopro.StocGameMessage.MsgSelectPosition;
/*
......
import { ygopro } from "../../../idl/ocgcore";
import { BufferReader } from "rust-src";
import { ygopro } from "../../../idl/ocgcore";
import MsgWin = ygopro.StocGameMessage.MsgWin;
/*
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { StocAdapter, YgoProPacket } from "../packet";
/*
* STOC HsPlayerChange
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { UTF16_BUFFER_MAX_LEN, _cutoff_name } from "../util";
import { StocAdapter, YgoProPacket } from "../packet";
import { _cutoff_name, UTF16_BUFFER_MAX_LEN } from "../util";
const UINT8_PER_UINT16 = 2;
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { StocAdapter, YgoProPacket } from "../packet";
/*
* STOC HsWatchChange
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { StocAdapter, YgoProPacket } from "../packet";
/*
* STOC JoinGame
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { StocAdapter, YgoProPacket } from "../packet";
/*
* STOC SelectHand
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { StocAdapter, YgoProPacket } from "../packet";
/*
* STOC SelectTp
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { BufferReader } from "rust-src";
import { ygopro } from "../../idl/ocgcore";
import { StocAdapter, YgoProPacket } from "../packet";
/*
* STOC TimeLimit
*
......
import { ygopro } from "../../idl/ocgcore";
import { YgoProPacket, StocAdapter } from "../packet";
import { StocAdapter, YgoProPacket } from "../packet";
/*
* STOC TypeChange
......
......@@ -2,20 +2,21 @@
* 一些发ygopro协议数据包的辅助函数,用于简化业务代码。
*
* */
import { ygopro } from "./idl/ocgcore";
import socketMiddleWare, { socketCmd } from "@/middleware/socket";
import { IDeck } from "../deck";
import PlayerInfoAdapter from "./ocgAdapter/ctos/ctosPlayerInfo";
import JoinGameAdapter from "./ocgAdapter/ctos/ctosJoinGame";
import UpdateDeckAdapter from "./ocgAdapter/ctos/ctosUpdateDeck";
import { ygopro } from "./idl/ocgcore";
import Chat from "./ocgAdapter/ctos/ctosChat";
import GameMsgResponse from "./ocgAdapter/ctos/ctosGameMsgResponse/mod";
import HandResult from "./ocgAdapter/ctos/ctosHandResult";
import HsReadyAdapter from "./ocgAdapter/ctos/ctosHsReady";
import HsStartAdapter from "./ocgAdapter/ctos/ctosHsStart";
import HandResult from "./ocgAdapter/ctos/ctosHandResult";
import TpResult from "./ocgAdapter/ctos/ctosTpResult";
import TimeConfirm from "./ocgAdapter/ctos/ctosTimeConfirm";
import JoinGameAdapter from "./ocgAdapter/ctos/ctosJoinGame";
import PlayerInfoAdapter from "./ocgAdapter/ctos/ctosPlayerInfo";
import Surrender from "./ocgAdapter/ctos/ctosSurrender";
import GameMsgResponse from "./ocgAdapter/ctos/ctosGameMsgResponse/mod";
import Chat from "./ocgAdapter/ctos/ctosChat";
import TimeConfirm from "./ocgAdapter/ctos/ctosTimeConfirm";
import TpResult from "./ocgAdapter/ctos/ctosTpResult";
import UpdateDeckAdapter from "./ocgAdapter/ctos/ctosUpdateDeck";
export function sendUpdateDeck(deck: IDeck) {
const updateDeck = new ygopro.YgoCtosMsg({
......
import axios from "axios";
import NeosConfig from "../../neos.config.json";
import { getCardStr, fetchCard } from "./cards";
import { useConfig } from "@/config";
import { fetchCard, getCardStr } from "./cards";
const NeosConfig = useConfig();
export const DESCRIPTION_LIMIT = 10000;
export async function initStrings() {
......
import { useEnv } from "@/hook";
import NeosDevConfig from "../../neos.config.json";
import NeosProdConfig from "../../neos.config.prod.json";
......
export * from "./useApp";
export * from "./useEnv";
export * from "./useMeshClick";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { RootState, AppDispatch } from "@/store";
import type { AppDispatch, RootState } from "@/store";
// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch: () => AppDispatch = useDispatch;
......
// 一些自定义`Hook`
import { ActionEvent } from "@babylonjs/core";
import { MutableRefObject, useEffect, useRef } from "react";
import { Nullable } from "@babylonjs/core/types.js";
import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh.js";
import { ActionManager } from "@babylonjs/core/Actions/actionManager.js";
import { IAction } from "@babylonjs/core/Actions/action.js";
import { ActionManager } from "@babylonjs/core/Actions/actionManager.js";
import { ExecuteCodeAction } from "@babylonjs/core/Actions/directActions.js";
import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh.js";
import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
import { Nullable } from "@babylonjs/core/types.js";
import { MutableRefObject, useEffect, useRef } from "react";
export interface MeshEventType {
(env: ActionEvent): void;
......
......@@ -19,14 +19,15 @@
* 在进行代码开发的时候需要注意这点。
*
* */
import { ConfigProvider, theme } from "antd";
import zhCN from "antd/locale/zh_CN";
import React from "react";
import ReactDOM from "react-dom/client";
import Neos from "./ui/Neos";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { BrowserRouter } from "react-router-dom";
import { store } from "./store";
import { ConfigProvider, theme } from "antd";
import zhCN from "antd/locale/zh_CN";
import Neos from "./ui/Neos";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
......
......@@ -4,8 +4,8 @@
* 所有长连接/Websocket相关的逻辑都应该收敛在这里。
*
* */
import handleSocketOpen from "../service/onSocketOpen";
import handleSocketMessage from "../service/onSocketMessage";
import handleSocketOpen from "../service/onSocketOpen";
export enum socketCmd {
// 建立长连接
......
......@@ -6,8 +6,11 @@
* */
import initSqlJs, { Database } from "sql.js";
import { CardMeta, CardData, CardText } from "@/api/cards";
import NeosConfig from "../../neos.config.json";
import { CardData, CardMeta, CardText } from "@/api/cards";
import { useConfig } from "@/config";
const NeosConfig = useConfig();
export enum sqliteCmd {
// 初始化
......
......@@ -3,6 +3,7 @@
*
* */
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "@/store";
export interface chatState {
......
import { judgeSelf } from "./util";
import {
PayloadAction,
CaseReducer,
ActionReducerMapBuilder,
CaseReducer,
PayloadAction,
} from "@reduxjs/toolkit";
import { DuelState } from "./mod";
import { RootState } from "@/store";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import {
createAsyncMetaThunk,
DuelFieldState,
extendState,
extendMeta,
removeCard,
DuelReducer,
Interactivity,
extendIdleInteractivities,
extendMeta,
extendState,
Interactivity,
removeCard,
} from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface ExclusionState extends DuelFieldState {}
export interface BanishedZoneState extends DuelFieldState {}
// 初始化除外区状态
export const initExclusionImpl: CaseReducer<
export const initBanishedZoneImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
const player = action.payload;
if (judgeSelf(player, state)) {
state.meExclusion = { inner: [] };
state.meBanishedZone = { inner: [] };
} else {
state.opExclusion = { inner: [] };
state.opBanishedZone = { inner: [] };
}
};
// 增加除外区
export const fetchExclusionMeta = createAsyncMetaThunk(
"duel/fetchExclusionMeta"
export const fetchBanishedZoneMeta = createAsyncMetaThunk(
"duel/fetchBanishedZoneMeta"
);
export const exclusionCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchExclusionMeta.pending, (state, action) => {
export const banishedZoneCase = (
builder: ActionReducerMapBuilder<DuelState>
) => {
builder.addCase(fetchBanishedZoneMeta.pending, (state, action) => {
// Meta结果没返回之前先更新`ID`
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
......@@ -56,51 +60,51 @@ export const exclusionCase = (builder: ActionReducerMapBuilder<DuelState>) => {
counters: {},
};
if (judgeSelf(controler, state)) {
extendState(state.meExclusion, newExclusion);
extendState(state.meBanishedZone, newExclusion);
} else {
extendState(state.opExclusion, newExclusion);
extendState(state.opBanishedZone, newExclusion);
}
});
builder.addCase(fetchExclusionMeta.fulfilled, (state, action) => {
builder.addCase(fetchBanishedZoneMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const meta = action.payload.meta;
if (judgeSelf(controler, state)) {
extendMeta(state.meExclusion, meta, sequence);
extendMeta(state.meBanishedZone, meta, sequence);
} else {
extendMeta(state.opExclusion, meta, sequence);
extendMeta(state.opBanishedZone, meta, sequence);
}
});
};
// 删除除外区
export const removeExclusionImpl: CaseReducer<
export const removeBanishedZoneImpl: CaseReducer<
DuelState,
PayloadAction<{ controler: number; sequence: number }>
> = (state, action) => {
const exclusion = judgeSelf(action.payload.controler, state)
? state.meExclusion
: state.opExclusion;
removeCard(exclusion, action.payload.sequence);
const banishedZone = judgeSelf(action.payload.controler, state)
? state.meBanishedZone
: state.opBanishedZone;
removeCard(banishedZone, action.payload.sequence);
};
export const addExclusionIdleInteractivitiesImpl: DuelReducer<{
export const addBanishedZoneIdleInteractivitiesImpl: DuelReducer<{
player: number;
sequence: number;
interactivity: Interactivity<number>;
}> = (state, action) => {
const exclusion = judgeSelf(action.payload.player, state)
? state.meExclusion
: state.opExclusion;
const banishedZone = judgeSelf(action.payload.player, state)
? state.meBanishedZone
: state.opBanishedZone;
extendIdleInteractivities(
exclusion,
banishedZone,
action.payload.sequence,
action.payload.interactivity
);
};
export const selectMeExclusion = (state: RootState) =>
state.duel.meExclusion || { inner: [] };
export const selectopExclusion = (state: RootState) =>
state.duel.opExclusion || { inner: [] };
export const selectMeBanishedZone = (state: RootState) =>
state.duel.meBanishedZone || { inner: [] };
export const selectOpBanishedZone = (state: RootState) =>
state.duel.opBanishedZone || { inner: [] };
import { judgeSelf } from "./util";
import {
PayloadAction,
CaseReducer,
ActionReducerMapBuilder,
CaseReducer,
PayloadAction,
} from "@reduxjs/toolkit";
import { DuelState } from "./mod";
import { RootState } from "@/store";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import {
createAsyncMetaThunk,
DuelFieldState,
extendState,
extendMeta,
removeCard,
DuelReducer,
Interactivity,
extendIdleInteractivities,
extendMeta,
extendState,
Interactivity,
removeCard,
} from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface CemeteryState extends DuelFieldState {}
export interface GraveyardState extends DuelFieldState {}
// 初始化墓地状态
export const initCemeteryImpl: CaseReducer<DuelState, PayloadAction<number>> = (
state,
action
) => {
export const initGraveyardImpl: CaseReducer<
DuelState,
PayloadAction<number>
> = (state, action) => {
const player = action.payload;
if (judgeSelf(player, state)) {
state.meCemetery = { inner: [] };
state.meGraveyard = { inner: [] };
} else {
state.opCemetery = { inner: [] };
state.opGraveyard = { inner: [] };
}
};
// 增加墓地
export const fetchCemeteryMeta = createAsyncMetaThunk("duel/fetchCemeteryMeta");
export const fetchGraveyardMeta = createAsyncMetaThunk(
"duel/fetchGraveyardMeta"
);
export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchCemeteryMeta.pending, (state, action) => {
export const graveyardCase = (builder: ActionReducerMapBuilder<DuelState>) => {
builder.addCase(fetchGraveyardMeta.pending, (state, action) => {
// Meta结果没返回之前先更新`ID`
const controler = action.meta.arg.controler;
const sequence = action.meta.arg.sequence;
const code = action.meta.arg.code;
const newCemetery = {
const newGraveyard = {
occupant: { id: code, data: {}, text: {} },
location: {
controler,
......@@ -54,51 +58,51 @@ export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => {
counters: {},
};
if (judgeSelf(controler, state)) {
extendState(state.meCemetery, newCemetery);
extendState(state.meGraveyard, newGraveyard);
} else {
extendState(state.opCemetery, newCemetery);
extendState(state.opGraveyard, newGraveyard);
}
});
builder.addCase(fetchCemeteryMeta.fulfilled, (state, action) => {
builder.addCase(fetchGraveyardMeta.fulfilled, (state, action) => {
const controler = action.payload.controler;
const sequence = action.payload.sequence;
const meta = action.payload.meta;
if (judgeSelf(controler, state)) {
extendMeta(state.meCemetery, meta, sequence);
extendMeta(state.meGraveyard, meta, sequence);
} else {
extendMeta(state.opCemetery, meta, sequence);
extendMeta(state.opGraveyard, meta, sequence);
}
});
};
// 删除墓地
export const removeCemeteryImpl: CaseReducer<
export const removeGraveyardImpl: CaseReducer<
DuelState,
PayloadAction<{ controler: number; sequence: number }>
> = (state, action) => {
const cemetery = judgeSelf(action.payload.controler, state)
? state.meCemetery
: state.opCemetery;
removeCard(cemetery, action.payload.sequence);
const graveyard = judgeSelf(action.payload.controler, state)
? state.meGraveyard
: state.opGraveyard;
removeCard(graveyard, action.payload.sequence);
};
export const addCemeteryIdleInteractivitiesImpl: DuelReducer<{
export const addGraveyardIdleInteractivitiesImpl: DuelReducer<{
player: number;
sequence: number;
interactivity: Interactivity<number>;
}> = (state, action) => {
const cemetery = judgeSelf(action.payload.player, state)
? state.meCemetery
: state.opCemetery;
const graveyard = judgeSelf(action.payload.player, state)
? state.meGraveyard
: state.opGraveyard;
extendIdleInteractivities(
cemetery,
graveyard,
action.payload.sequence,
action.payload.interactivity
);
};
export const selectMeCemetery = (state: RootState) =>
state.duel.meCemetery || { inner: [] };
export const selectOpCemetery = (state: RootState) =>
state.duel.opCemetery || { inner: [] };
export const selectMeGraveyard = (state: RootState) =>
state.duel.meGraveyard || { inner: [] };
export const selectOpGraveyard = (state: RootState) =>
state.duel.opGraveyard || { inner: [] };
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import {
clearIdleInteractivities,
clearPlaceInteractivities,
......@@ -23,16 +24,16 @@ export const clearAllIdleInteractivitiesImpl: DuelReducer<number> = (
state.meHands,
state.meMonsters,
state.meMagics,
state.meCemetery,
state.meExclusion,
state.meGraveyard,
state.meBanishedZone,
state.meExtraDeck,
]
: [
state.opHands,
state.opMonsters,
state.opMagics,
state.opCemetery,
state.opExclusion,
state.opGraveyard,
state.opBanishedZone,
state.opExtraDeck,
];
......@@ -50,15 +51,15 @@ export const clearAllPlaceInteractivitiesImpl: DuelReducer<number> = (
state.meHands,
state.meMonsters,
state.meMagics,
state.meCemetery,
state.meExclusion,
state.meGraveyard,
state.meBanishedZone,
]
: [
state.opHands,
state.opMonsters,
state.opMagics,
state.opCemetery,
state.opExclusion,
state.opGraveyard,
state.opBanishedZone,
];
states.forEach((item) => clearPlaceInteractivities(item));
......@@ -105,18 +106,18 @@ export const updateFieldDataImpl: DuelReducer<MsgUpdateData> = (
break;
}
case ygopro.CardZone.GRAVE: {
const cemetery = judgeSelf(player, state)
? state.meCemetery
: state.opCemetery;
updateCardData(cemetery, actions);
const graveyard = judgeSelf(player, state)
? state.meGraveyard
: state.opGraveyard;
updateCardData(graveyard, actions);
break;
}
case ygopro.CardZone.REMOVED: {
const exclusion = judgeSelf(player, state)
? state.meExclusion
: state.opExclusion;
updateCardData(exclusion, actions);
const BanishedZone = judgeSelf(player, state)
? state.meBanishedZone
: state.opBanishedZone;
updateCardData(BanishedZone, actions);
break;
}
......@@ -139,10 +140,10 @@ export const reloadFieldImpl: DuelReducer<MsgReloadField> = (state, action) => {
state.opMonsters = { inner: [] };
state.meMagics = { inner: [] };
state.opMagics = { inner: [] };
state.meCemetery = { inner: [] };
state.opCemetery = { inner: [] };
state.meExclusion = { inner: [] };
state.opExclusion = { inner: [] };
state.meGraveyard = { inner: [] };
state.opGraveyard = { inner: [] };
state.meBanishedZone = { inner: [] };
state.opBanishedZone = { inner: [] };
state.meHands = { inner: [] };
state.opHands = { inner: [] };
......@@ -182,20 +183,20 @@ export const reloadFieldImpl: DuelReducer<MsgReloadField> = (state, action) => {
player
);
// GRAVE
const cemetery = judgeSelf(player, state)
? state.meCemetery
: state.opCemetery;
const graveyard = judgeSelf(player, state)
? state.meGraveyard
: state.opGraveyard;
reloadFieldMeta(
cemetery,
graveyard,
reload.zone_actions.filter((item) => item.zone == ygopro.CardZone.GRAVE),
player
);
// REMOVED
const exclusion = judgeSelf(player, state)
? state.meExclusion
: state.opExclusion;
const banishedZone = judgeSelf(player, state)
? state.meBanishedZone
: state.opBanishedZone;
reloadFieldMeta(
exclusion,
banishedZone,
reload.zone_actions.filter(
(item) => item.zone == ygopro.CardZone.REMOVED
),
......
import { judgeSelf } from "./util";
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit";
import { DuelState } from "./mod";
import { RootState } from "@/store";
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { DuelFieldState, CardState } from "./generic";
import { RootState } from "@/store";
import { CardState, DuelFieldState } from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface DeckState extends DuelFieldState {}
......
import { ActionReducerMapBuilder } from "@reduxjs/toolkit";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import {
createAsyncMetaThunk,
createAsyncRepeatedMetaThunk,
......
......@@ -4,8 +4,10 @@ import {
createAsyncThunk,
PayloadAction,
} from "@reduxjs/toolkit";
import { CardMeta, fetchCard } from "@/api/cards";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { DuelState } from "./mod";
import ReloadFieldAction = ygopro.StocGameMessage.MsgReloadField.ZoneAction;
type UpdateDataAction = ReturnType<
......
......@@ -3,20 +3,22 @@ import {
CaseReducer,
PayloadAction,
} from "@reduxjs/toolkit";
import { DuelState } from "./mod";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import { judgeSelf } from "./util";
import {
Interactivity,
DuelFieldState,
removeCard,
createAsyncMetaThunk,
insertCard,
extendMeta,
createAsyncRepeatedMetaThunk,
DuelFieldState,
extendMeta,
insertCard,
Interactivity,
removeCard,
updateCardMeta,
} from "./generic";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface HandState extends DuelFieldState {}
......
import { createAsyncThunk, ActionReducerMapBuilder } from "@reduxjs/toolkit";
import { DuelState } from "./mod";
import { RootState } from "@/store";
import { DESCRIPTION_LIMIT, fetchStrings, getStrings } from "@/api/strings";
import { findCardByLocation } from "./util";
import { ActionReducerMapBuilder, createAsyncThunk } from "@reduxjs/toolkit";
import { fetchCard } from "@/api/cards";
import { DuelReducer } from "./generic";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { DESCRIPTION_LIMIT, fetchStrings, getStrings } from "@/api/strings";
import { RootState } from "@/store";
import { DuelReducer } from "./generic";
import { DuelState } from "./mod";
import { findCardByLocation } from "./util";
export interface HintState {
code: number;
......
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit";
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
import MsgUpdateHp = ygopro.StocGameMessage.MsgUpdateHp;
......
import { judgeSelf } from "./util";
import {
PayloadAction,
CaseReducer,
ActionReducerMapBuilder,
CaseReducer,
PayloadAction,
} from "@reduxjs/toolkit";
import { DuelState } from "./mod";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import {
clearIdleInteractivities,
clearPlaceInteractivities,
createAsyncMetaThunk,
DuelFieldState,
extendIdleInteractivities,
extendOccupant,
extendPlaceInteractivity,
clearPlaceInteractivities,
removeOccupant,
Interactivity,
extendIdleInteractivities,
clearIdleInteractivities,
removeOccupant,
setPosition,
} from "./generic";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
export interface MagicState extends DuelFieldState {}
......
......@@ -3,121 +3,123 @@
*
* */
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { InitInfo, infoInitImpl, updateHpImpl } from "./initInfoSlice";
import { TimeLimit, updateTimeLimitImpl } from "./timeLimit";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import {
addBanishedZoneIdleInteractivitiesImpl,
banishedZoneCase,
BanishedZoneState,
initBanishedZoneImpl,
removeBanishedZoneImpl,
} from "./banishedZoneSlice";
import {
addGraveyardIdleInteractivitiesImpl,
graveyardCase,
GraveyardState,
initGraveyardImpl,
removeGraveyardImpl,
} from "./cemeretySlice";
import {
clearAllIdleInteractivitiesImpl,
clearAllPlaceInteractivitiesImpl,
reloadFieldImpl,
updateFieldDataImpl,
} from "./commonSlice";
import { DeckState, initDeckImpl } from "./deckSlice";
import {
addExtraDeckIdleInteractivitiesImpl,
extraDeckCase,
ExtraDeckState,
removeExtraDeckImpl,
} from "./extraDeckSlice";
import {
HandState,
handsCase,
clearHandsIdleInteractivityImpl,
addHandsIdleInteractivityImpl,
clearHandsIdleInteractivityImpl,
handsCase,
HandState,
removeHandImpl,
} from "./handsSlice";
import { newTurnImpl } from "./turnSlice";
import { hintCase, HintState, initHintImpl } from "./hintSlice";
import { infoInitImpl, InitInfo, updateHpImpl } from "./initInfoSlice";
import {
newPhaseImpl,
PhaseState,
setEnableBpImpl,
setEnableM2Impl,
setEnableEpImpl,
} from "./phaseSlice";
import { RootState } from "@/store";
import { HintState, hintCase, initHintImpl } from "./hintSlice";
addMagicIdleInteractivitiesImpl,
addMagicPlaceInteractivitiesImpl,
clearMagicIdleInteractivitiesImpl,
clearMagicPlaceInteractivitiesImpl,
initMagicsImpl,
magicCase,
MagicState,
removeMagicImpl,
setMagicPositionImpl,
} from "./magicSlice";
import {
checkCardModalCase,
checkCardModalV2Case,
checkCardModalV3Case,
clearCheckCounterImpl,
ModalState,
optionModalCase,
resetCheckCardModalImpl,
resetCheckCardModalV2Impl,
resetCheckCardModalV3Impl,
resetOptionModalImpl,
resetPositionModalImpl,
resetSortCardModalImpl,
setCardListModalInfoImpl,
setCardListModalIsOpenImpl,
setCardModalCountersImpl,
setCardModalInteractiviesImpl,
setCardModalIsOpenImpl,
setCardModalMetaImpl,
setCardModalInteractiviesImpl,
setCardListModalIsOpenImpl,
setCardListModalInfoImpl,
setCheckCardMOdalCancelAbleImpl,
setCheckCardModalCancelResponseImpl,
setCheckCardModalIsOpenImpl,
setCheckCardModalMinMaxImpl,
setCheckCardModalOnSubmitImpl,
setCheckCardMOdalCancelAbleImpl,
setCheckCardModalCancelResponseImpl,
resetCheckCardModalImpl,
setYesNoModalIsOpenImpl,
checkCardModalCase,
YesNoModalCase,
setPositionModalIsOpenImpl,
setPositionModalPositionsImpl,
resetPositionModalImpl,
setOptionModalIsOpenImpl,
resetOptionModalImpl,
optionModalCase,
setCheckCardModalV2IsOpenImpl,
setCheckCardModalV2CancelAbleImpl,
setCheckCardModalV2MinMaxImpl,
setCheckCardModalV2FinishAbleImpl,
resetCheckCardModalV2Impl,
setCheckCardModalV2IsOpenImpl,
setCheckCardModalV2MinMaxImpl,
setCheckCardModalV2ResponseAbleImpl,
checkCardModalV2Case,
setCheckCardModalV3AllLevelImpl,
setCheckCardModalV3IsOpenImpl,
setCheckCardModalV3MinMaxImpl,
setCheckCardModalV3AllLevelImpl,
setCheckCardModalV3OverFlowImpl,
setCheckCardModalV3ResponseAbleImpl,
resetCheckCardModalV3Impl,
checkCardModalV3Case,
setCardModalCountersImpl,
setCheckCounterImpl,
clearCheckCounterImpl,
setOptionModalIsOpenImpl,
setPositionModalIsOpenImpl,
setPositionModalPositionsImpl,
setSortCardModalIsOpenImpl,
resetSortCardModalImpl,
setYesNoModalIsOpenImpl,
sortCardModalCase,
YesNoModalCase,
} from "./modal/mod";
import {
MonsterState,
initMonstersImpl,
addMonsterPlaceInteractivitiesImpl,
clearMonsterPlaceInteractivitiesImpl,
addMonsterIdleInteractivitiesImpl,
addMonsterPlaceInteractivitiesImpl,
clearMonsterIdleInteractivitiesImpl,
clearMonsterPlaceInteractivitiesImpl,
initMonstersImpl,
monsterCase,
MonsterState,
removeMonsterImpl,
setMonsterPositionImpl,
removeOverlayImpl,
setMonsterPositionImpl,
updateMonsterCountersImpl,
monsterCase,
} from "./monstersSlice";
import {
MagicState,
initMagicsImpl,
addMagicPlaceInteractivitiesImpl,
clearMagicPlaceInteractivitiesImpl,
addMagicIdleInteractivitiesImpl,
clearMagicIdleInteractivitiesImpl,
removeMagicImpl,
setMagicPositionImpl,
magicCase,
} from "./magicSlice";
import {
CemeteryState,
initCemeteryImpl,
removeCemeteryImpl,
cemeteryCase,
addCemeteryIdleInteractivitiesImpl,
} from "./cemeretySlice";
import {
ExclusionState,
initExclusionImpl,
removeExclusionImpl,
exclusionCase,
addExclusionIdleInteractivitiesImpl,
} from "./exclusionSlice";
import { DeckState, initDeckImpl } from "./deckSlice";
import {
clearAllIdleInteractivitiesImpl,
clearAllPlaceInteractivitiesImpl,
updateFieldDataImpl,
reloadFieldImpl,
} from "./commonSlice";
import {
ExtraDeckState,
extraDeckCase,
removeExtraDeckImpl,
addExtraDeckIdleInteractivitiesImpl,
} from "./extraDeckSlice";
newPhaseImpl,
PhaseState,
setEnableBpImpl,
setEnableEpImpl,
setEnableM2Impl,
} from "./phaseSlice";
import { TimeLimit, updateTimeLimitImpl } from "./timeLimit";
import { newTurnImpl } from "./turnSlice";
import MsgWin = ygopro.StocGameMessage.MsgWin;
export interface DuelState {
......@@ -134,11 +136,11 @@ export interface DuelState {
meMagics?: MagicState; // 自己的魔法陷阱区状态
opMagics?: MagicState; // 对手的魔法陷阱区状态
meCemetery?: CemeteryState; // 自己的墓地状态
opCemetery?: CemeteryState; // 对手的墓地状态
meGraveyard?: GraveyardState; // 自己的墓地状态
opGraveyard?: GraveyardState; // 对手的墓地状态
meExclusion?: ExclusionState; // 自己的除外区状态
opExclusion?: ExclusionState; // 对手的除外区状态
meBanishedZone?: BanishedZoneState; // 自己的除外区状态
opBanishedZone?: BanishedZoneState; // 对手的除外区状态
meDeck?: DeckState; // 自己的卡组状态
opDeck?: DeckState; // 对手的卡组状态
......@@ -237,14 +239,14 @@ const duelSlice = createSlice({
removeMagic: removeMagicImpl,
// 墓地相关`Reducer`
initCemetery: initCemeteryImpl,
removeCemetery: removeCemeteryImpl,
addCemeteryIdleInteractivities: addCemeteryIdleInteractivitiesImpl,
initGraveyard: initGraveyardImpl,
removeGraveyard: removeGraveyardImpl,
addGraveyardIdleInteractivities: addGraveyardIdleInteractivitiesImpl,
// 除外区相关`Reducer`
initExclusion: initExclusionImpl,
removeExclusion: removeExclusionImpl,
addExclusionIdleInteractivities: addExclusionIdleInteractivitiesImpl,
initBanishedZone: initBanishedZoneImpl,
removeBanishedZone: removeBanishedZoneImpl,
addBanishedZoneIdleInteractivities: addBanishedZoneIdleInteractivitiesImpl,
// 卡组相关`Reducer`
initDeck: initDeckImpl,
......@@ -324,8 +326,8 @@ const duelSlice = createSlice({
hintCase(builder);
monsterCase(builder);
magicCase(builder);
cemeteryCase(builder);
exclusionCase(builder);
graveyardCase(builder);
banishedZoneCase(builder);
extraDeckCase(builder);
checkCardModalCase(builder);
YesNoModalCase(builder);
......@@ -368,9 +370,9 @@ export const {
setMagicPosition,
removeMagic,
removeHand,
initCemetery,
removeCemetery,
addCemeteryIdleInteractivities,
initGraveyard,
removeGraveyard,
addGraveyardIdleInteractivities,
setCardListModalIsOpen,
setCardListModalInfo,
setCheckCardModalIsOpen,
......@@ -388,9 +390,9 @@ export const {
initDeck,
removeExtraDeck,
addExtraDeckIdleInteractivities,
initExclusion,
removeExclusion,
addExclusionIdleInteractivities,
initBanishedZone,
removeBanishedZone,
addBanishedZoneIdleInteractivities,
setCheckCardModalV2IsOpen,
setCheckCardModalV2MinMax,
setCheckCardModalV2CancelAble,
......
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit";
import { DuelState } from "../mod";
import { RootState } from "@/store";
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { CardMeta } from "@/api/cards";
import { RootState } from "@/store";
import { DuelState } from "../mod";
// 更新卡牌列表弹窗打开状态
export const setCardListModalIsOpenImpl: CaseReducer<
......
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit";
import { DuelState } from "../mod";
import { RootState } from "@/store";
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { CardMeta } from "@/api/cards";
import { RootState } from "@/store";
import { DuelState } from "../mod";
// 更新卡牌弹窗打开状态
export const setCardModalIsOpenImpl: CaseReducer<
......
import {
PayloadAction,
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
ActionReducerMapBuilder,
PayloadAction,
} from "@reduxjs/toolkit";
import { fetchCard, getCardStr } from "@/api/cards";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import { DuelState } from "../mod";
import { cmpCardLocation, findCardByLocation, judgeSelf } from "../util";
import { fetchCard, getCardStr } from "@/api/cards";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
// 更新卡牌选择弹窗打开状态
export const setCheckCardModalIsOpenImpl: CaseReducer<
......
import { DuelState } from "../mod";
import { DuelReducer } from "../generic";
import {
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
} from "@reduxjs/toolkit";
import { fetchCard } from "@/api/cards";
import { RootState } from "@/store";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import { DuelReducer } from "../generic";
import { DuelState } from "../mod";
import { findCardByLocation } from "../util";
// 更新打开状态
......
import { DuelState } from "../mod";
import { DuelReducer } from "../generic";
import {
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
} from "@reduxjs/toolkit";
import { fetchCard } from "@/api/cards";
import { RootState } from "@/store";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import { DuelReducer } from "../generic";
import { DuelState } from "../mod";
import { findCardByLocation } from "../util";
// 更新打开状态
......
// 后续对于`MSG_SELECT_XXX`的处理UI都尽量用`Babylon.js`实现而不会通过`Antd`的`Modal`实现,因此这里不追求工程质量,暂时简单实现下。
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit";
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import { DuelState } from "../mod";
import { findCardByLocation } from "../util";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
type SelectCounter = ReturnType<
typeof ygopro.StocGameMessage.MsgSelectCounter.prototype.toObject
>;
......
......@@ -114,13 +114,13 @@ export interface ModalState {
};
}
export * from "./cardModalSlice";
export * from "./cardListModalSlice";
export * from "./cardModalSlice";
export * from "./checkCardModalSlice";
export * from "./yesNoModalSlice";
export * from "./positionModalSlice";
export * from "./optionModalSlice";
export * from "./checkCardModalV2Slice";
export * from "./checkCardModalV3Slice";
export * from "./checkCounterModalSlice";
export * from "./optionModalSlice";
export * from "./positionModalSlice";
export * from "./sortCardModalSlice";
export * from "./yesNoModalSlice";
import {
PayloadAction,
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
ActionReducerMapBuilder,
PayloadAction,
} from "@reduxjs/toolkit";
import { DuelState } from "../mod";
import { fetchCard, getCardStr } from "@/api/cards";
import { RootState } from "@/store";
import { DuelState } from "../mod";
export const setOptionModalIsOpenImpl: CaseReducer<
DuelState,
PayloadAction<boolean>
......
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit";
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import { DuelState } from "../mod";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
export const setPositionModalIsOpenImpl: CaseReducer<
DuelState,
......
......@@ -3,9 +3,11 @@ import {
CaseReducer,
createAsyncThunk,
} from "@reduxjs/toolkit";
import { fetchCard } from "@/api/cards";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import { DuelReducer } from "../generic";
import { DuelState } from "../mod";
type SortCard = ReturnType<
......
import {
PayloadAction,
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
ActionReducerMapBuilder,
PayloadAction,
} from "@reduxjs/toolkit";
import { CardMeta, fetchCard } from "@/api/cards";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { fetchStrings, getStrings } from "@/api/strings";
import { RootState } from "@/store";
import { DuelState } from "../mod";
// 更新YesNo弹窗是否打开状态
......
import { judgeSelf } from "./util";
import {
PayloadAction,
CaseReducer,
ActionReducerMapBuilder,
CaseReducer,
createAsyncThunk,
PayloadAction,
} from "@reduxjs/toolkit";
import { DuelState } from "./mod";
import { fetchCard } from "@/api/cards";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { RootState } from "@/store";
import {
DuelFieldState,
Interactivity,
clearIdleInteractivities,
clearPlaceInteractivities,
createAsyncMetaThunk,
DuelFieldState,
extendIdleInteractivities,
extendOccupant,
extendPlaceInteractivity,
clearPlaceInteractivities,
Interactivity,
removeOccupant,
extendIdleInteractivities,
clearIdleInteractivities,
setPosition,
removeOverlay,
setPosition,
} from "./generic";
import { fetchCard } from "@/api/cards";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
type MsgUpdateCounter = ReturnType<
typeof ygopro.StocGameMessage.MsgUpdateCounter.prototype.toObject
>;
......
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit";
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "@/store";
import { DuelState } from "./mod";
export interface PhaseState {
......
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit";
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
......
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit";
import { CaseReducer, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "@/store";
import { DuelState } from "./mod";
import { judgeSelf } from "./util";
......
......@@ -3,10 +3,12 @@
*
* */
import { DuelState } from "./mod";
import { Draft } from "@reduxjs/toolkit";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { CardState } from "./generic";
import { DuelState } from "./mod";
type Location =
| ygopro.CardLocation
......@@ -59,15 +61,15 @@ export function findCardByLocation(
return magics?.inner.find(finder);
}
case ygopro.CardZone.REMOVED: {
const exclusions = judgeSelf(controler, state)
? state.meExclusion
: state.opExclusion;
return exclusions?.inner.find(finder);
const banishedZones = judgeSelf(controler, state)
? state.meBanishedZone
: state.opBanishedZone;
return banishedZones?.inner.find(finder);
}
case ygopro.CardZone.GRAVE: {
const cemerety = judgeSelf(controler, state)
? state.meCemetery
: state.opCemetery;
? state.meGraveyard
: state.opGraveyard;
return cemerety?.inner.find(finder);
}
default: {
......
......@@ -3,6 +3,7 @@
*
* */
import { createSlice } from "@reduxjs/toolkit";
import { RootState } from "@/store";
export interface JoinState {
......
......@@ -3,6 +3,7 @@
*
* */
import { createSlice } from "@reduxjs/toolkit";
import { RootState } from "@/store";
export interface moraState {
......
......@@ -3,6 +3,7 @@
*
* */
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { RootState } from "@/store";
export interface Player {
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { AppDispatch } from "@/store";
import { fetchHandsMeta } from "@/reducers/duel/handsSlice";
import { fetchEsHintMeta } from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
export default (
draw: ygopro.StocGameMessage.MsgDraw,
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { setWaiting } from "@/reducers/duel/mod";
import { store } from "@/store";
import onMsgStart from "./start";
import onMsgAttack from "./attack";
import onMsgAttackDisable from "./attackDisable";
import onMsgChaining from "./chaining";
import onMsgDraw from "./draw";
import onMsgNewTurn from "./newTurn";
import onMsgNewPhase from "./newPhase";
import onMsgFilpSummoned from "./flipSummoned";
import onMsgFlipSummoning from "./flipSummoning";
import onMsgHint from "./hint";
import onMsgSelectIdleCmd from "./selectIdleCmd";
import onMsgSelectPlace from "./selectPlace";
import onMsgMove from "./move";
import onMsgNewPhase from "./newPhase";
import onMsgNewTurn from "./newTurn";
import onMsgPosChange from "./posChange";
import onMsgReloadField from "./reloadField";
import onMsgSelectBattleCmd from "./selectBattleCmd";
import onMsgSelectCard from "./selectCard";
import onMsgSelectChain from "./selectChain";
import onMsgSelectCounter from "./selectCounter";
import onMsgSelectEffectYn from "./selectEffectYn";
import onMsgSelectPosition from "./selectPosition";
import onMsgSelectIdleCmd from "./selectIdleCmd";
import onMsgSelectOption from "./selectOption";
import onMsgShuffleHand from "./shuffleHand";
import onMsgSelectBattleCmd from "./selectBattleCmd";
import onMsgPosChange from "./posChange";
import onMsgSelectUnselectCard from "./selectUnselectCard";
import onMsgSelectYesNo from "./selectYesNo";
import onMsgUpdateHp from "./updateHp";
import onMsgWin from "./win";
import onMsgWait from "./wait";
import onUnimplemented from "./unimplemented";
import onMsgUpdateData from "./updateData";
import onMsgReloadField from "./reloadField";
import onMsgSelectPlace from "./selectPlace";
import onMsgSelectPosition from "./selectPosition";
import onMsgSelectSum from "./selectSum";
import onMsgSelectTribute from "./selectTribute";
import onMsgUpdateCounter from "./updateCounter";
import onMsgSelectCounter from "./selectCounter";
import onMsgSortCard from "./sortCard";
import onMsgSelectUnselectCard from "./selectUnselectCard";
import onMsgSelectYesNo from "./selectYesNo";
import onMsgSet from "./set";
import onMsgSwap from "./swap";
import onMsgAttack from "./attack";
import onMsgAttackDisable from "./attackDisable";
import onMsgChaining from "./chaining";
import onMsgFlipSummoning from "./flipSummoning";
import onMsgFilpSummoned from "./flipSummoned";
import onMsgSpSummoning from "./spSummoning";
import onMsgShuffleHand from "./shuffleHand";
import onMsgSortCard from "./sortCard";
import onMsgSpSummoned from "./spSummoned";
import onMsgSummoning from "./summoning";
import onMsgSpSummoning from "./spSummoning";
import onMsgStart from "./start";
import onMsgSummoned from "./summoned";
import { setWaiting } from "@/reducers/duel/mod";
import onMsgSummoning from "./summoning";
import onMsgSwap from "./swap";
import onUnimplemented from "./unimplemented";
import onMsgUpdateCounter from "./updateCounter";
import onMsgUpdateData from "./updateData";
import onMsgUpdateHp from "./updateHp";
import onMsgWait from "./wait";
import onMsgWin from "./win";
const ActiveList = [
"select_idle_cmd",
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { AppDispatch } from "@/store";
import {
fetchCommonHintMeta,
fetchEsHintMeta,
fetchSelectHintMeta,
} from "@/reducers/duel/hintSlice";
import { AppDispatch } from "@/store";
import MsgHint = ygopro.StocGameMessage.MsgHint;
export default (hint: MsgHint, dispatch: AppDispatch) => {
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import MsgMove = ygopro.StocGameMessage.MsgMove;
import { AppDispatch } from "@/store";
import {
fetchMonsterMeta,
fetchOverlayMeta,
} from "@/reducers/duel/monstersSlice";
import { fetchBanishedZoneMeta } from "@/reducers/duel/banishedZoneSlice";
import { fetchGraveyardMeta } from "@/reducers/duel/cemeretySlice";
import { fetchExtraDeckMeta } from "@/reducers/duel/extraDeckSlice";
import { insertHandMeta } from "@/reducers/duel/handsSlice";
import { fetchMagicMeta } from "@/reducers/duel/magicSlice";
import {
removeCemetery,
removeExclusion,
removeBanishedZone,
removeExtraDeck,
removeGraveyard,
removeHand,
removeMagic,
removeMonster,
removeOverlay,
} from "@/reducers/duel/mod";
import { fetchMagicMeta } from "@/reducers/duel/magicSlice";
import { fetchCemeteryMeta } from "@/reducers/duel/cemeretySlice";
import { insertHandMeta } from "@/reducers/duel/handsSlice";
import { fetchExclusionMeta } from "@/reducers/duel/exclusionSlice";
import { fetchExtraDeckMeta } from "@/reducers/duel/extraDeckSlice";
import {
fetchMonsterMeta,
fetchOverlayMeta,
} from "@/reducers/duel/monstersSlice";
import { AppDispatch } from "@/store";
import { REASON_MATERIAL } from "../../common";
const OVERLAY_STACK: { code: number; sequence: number }[] = [];
......@@ -51,14 +52,17 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
}
case ygopro.CardZone.GRAVE: {
dispatch(
removeCemetery({ controler: from.controler, sequence: from.sequence })
removeGraveyard({ controler: from.controler, sequence: from.sequence })
);
break;
}
case ygopro.CardZone.REMOVED: {
dispatch(
removeExclusion({ controler: from.controler, sequence: from.sequence })
removeBanishedZone({
controler: from.controler,
sequence: from.sequence,
})
);
break;
......@@ -127,7 +131,7 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
}
case ygopro.CardZone.GRAVE: {
dispatch(
fetchCemeteryMeta({
fetchGraveyardMeta({
controler: to.controler,
sequence: to.sequence,
code,
......@@ -145,7 +149,7 @@ export default (move: MsgMove, dispatch: AppDispatch) => {
}
case ygopro.CardZone.REMOVED: {
dispatch(
fetchExclusionMeta({
fetchBanishedZoneMeta({
controler: to.controler,
sequence: to.sequence,
code,
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { AppDispatch } from "@/store";
import { updatePhase } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
export default (
newPhase: ygopro.StocGameMessage.MsgNewPhase,
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { AppDispatch } from "@/store";
import { updateTurn } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
export default (
newTurn: ygopro.StocGameMessage.MsgNewTurn,
......
import { ActionCreatorWithPayload } from "@reduxjs/toolkit";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { Interactivity, InteractType } from "@/reducers/duel/generic";
import {
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { AppDispatch } from "@/store";
import {
setCheckCardModalIsOpen,
setCheckCardModalMinMax,
setCheckCardModalOnSubmit,
} from "@/reducers/duel/mod";
import { fetchCheckCardMeta } from "@/reducers/duel/modal/mod";
import { AppDispatch } from "@/store";
import MsgSelectCard = ygopro.StocGameMessage.MsgSelectCard;
import { CardZoneToChinese } from "./util";
......
......@@ -10,6 +10,7 @@ import {
} from "@/reducers/duel/mod";
import { fetchCheckCardMeta } from "@/reducers/duel/modal/mod";
import { AppDispatch } from "@/store";
import { CardZoneToChinese } from "./util";
import MsgSelectChain = ygopro.StocGameMessage.MsgSelectChain;
......
......@@ -3,6 +3,7 @@ import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { setYesNoModalIsOpen } from "@/reducers/duel/mod";
import { fetchYesNoMeta } from "@/reducers/duel/modal/mod";
import { AppDispatch } from "@/store";
import { CardZoneToChinese } from "./util";
import MsgSelectEffectYn = ygopro.StocGameMessage.MsgSelectEffectYn;
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { AppDispatch } from "@/store";
import { Interactivity, InteractType } from "@/reducers/duel/generic";
import {
addBanishedZoneIdleInteractivities,
addExtraDeckIdleInteractivities,
addGraveyardIdleInteractivities,
addHandsIdleInteractivity,
addMonsterIdleInteractivities,
addMagicIdleInteractivities,
addMonsterIdleInteractivities,
clearAllIdleInteractivities,
setEnableBp,
setEnableEp,
addCemeteryIdleInteractivities,
clearAllIdleInteractivities,
addExclusionIdleInteractivities,
addExtraDeckIdleInteractivities,
} from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import MsgSelectIdleCmd = ygopro.StocGameMessage.MsgSelectIdleCmd;
import { ActionCreatorWithPayload } from "@reduxjs/toolkit";
......@@ -83,12 +83,12 @@ export default (selectIdleCmd: MsgSelectIdleCmd, dispatch: AppDispatch) => {
break;
}
case ygopro.CardZone.GRAVE: {
dispatcher(data, interactType, addCemeteryIdleInteractivities);
dispatcher(data, interactType, addGraveyardIdleInteractivities);
break;
}
case ygopro.CardZone.REMOVED: {
dispatcher(data, interactType, addExclusionIdleInteractivities);
dispatcher(data, interactType, addBanishedZoneIdleInteractivities);
break;
}
......
......@@ -2,8 +2,8 @@ import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { AppDispatch } from "@/store";
import MsgSelectPlace = ygopro.StocGameMessage.MsgSelectPlace;
import {
addMonsterPlaceInteractivities,
addMagicPlaceInteractivities,
addMonsterPlaceInteractivities,
} from "@/reducers/duel/mod";
export default (selectPlace: MsgSelectPlace, dispatch: AppDispatch) => {
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { AppDispatch } from "@/store";
import {
infoInit,
setSelfType,
initMonsters,
initMagics,
initCemetery,
initBanishedZone,
initDeck,
initExclusion,
initGraveyard,
initHint,
initMagics,
initMonsters,
setSelfType,
} from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
export default (
start: ygopro.StocGameMessage.MsgStart,
......@@ -40,11 +40,11 @@ export default (
dispatch(initMonsters(1));
dispatch(initMagics(0));
dispatch(initMagics(1));
dispatch(initCemetery(0));
dispatch(initCemetery(1));
dispatch(initGraveyard(0));
dispatch(initGraveyard(1));
dispatch(initDeck({ player: 0, deskSize: start.deckSize1 }));
dispatch(initDeck({ player: 1, deskSize: start.deckSize2 }));
dispatch(initExclusion(0));
dispatch(initExclusion(1));
dispatch(initBanishedZone(0));
dispatch(initBanishedZone(1));
dispatch(initHint());
};
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { store } from "@/store";
import { updateTimeLimit } from "@/reducers/duel/mod";
import { sendTimeConfirm } from "@/api/ocgcore/ocgHelper";
import { updateTimeLimit } from "@/reducers/duel/mod";
import { store } from "@/store";
export default function handleTimeLimit(timeLimit: ygopro.StocTimeLimit) {
const dispatch = store.dispatch;
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { useConfig } from "@/config";
import { setUnimplemented } from "@/reducers/duel/mod";
import { AppDispatch } from "@/store";
import NeosConfig from "../../../neos.config.json";
const NeosConfig = useConfig();
export default (
unimplemented: ygopro.StocGameMessage.MsgUnimplemented,
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { store } from "@/store";
import { player0DeckInfo, player1DeckInfo } from "@/reducers/playerSlice";
import { store } from "@/store";
// FIXME: player0 不一定是当前玩家
export default function handleDeckCount(pb: ygopro.YgoStocMsg) {
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { store } from "@/store";
import { selectHandAble } from "@/reducers/moraSlice";
import { store } from "@/store";
export default function handleSelectHand(_: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { store } from "@/store";
import { selectTpAble } from "@/reducers/moraSlice";
import { store } from "@/store";
export default function handleSelectTp(_: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
......
......@@ -2,20 +2,21 @@
* 长连接消息事件订阅处理逻辑
*
* */
import handleHsPlayerChange from "./room/hsPlayerChange";
import handleTypeChange from "./room/typeChange";
import handleHsPlayerEnter from "./room/hsPlayerEnter";
import handleJoinGame from "./room/joinGame";
import handleChat from "./room/chat";
import handleHsWatchChange from "./room/hsWatchChange";
import { YgoProPacket } from "@/api/ocgcore/ocgAdapter/packet";
import { adaptStoc } from "@/api/ocgcore/ocgAdapter/adapter";
import handleSelectHand from "./mora/selectHand";
import handleSelectTp from "./mora/selectTp";
import handleDeckCount from "./mora/deckCount";
import { YgoProPacket } from "@/api/ocgcore/ocgAdapter/packet";
import handleGameMsg from "./duel/gameMsg";
import handleTimeLimit from "./duel/timeLimit";
import handleDeckCount from "./mora/deckCount";
import handleSelectHand from "./mora/selectHand";
import handleSelectTp from "./mora/selectTp";
import handleChat from "./room/chat";
import handleDuelStart from "./room/duelStart";
import handleHsPlayerChange from "./room/hsPlayerChange";
import handleHsPlayerEnter from "./room/hsPlayerEnter";
import handleHsWatchChange from "./room/hsWatchChange";
import handleJoinGame from "./room/joinGame";
import handleTypeChange from "./room/typeChange";
/*
* 先将从长连接中读取到的二进制数据通过Adapter转成protobuf结构体,
......
......@@ -3,8 +3,9 @@
*
* */
import { sendJoinGame, sendPlayerInfo } from "@/api/ocgcore/ocgHelper";
import NeosConfig from "../../neos.config.json";
import { useConfig } from "@/config";
const NeosConfig = useConfig();
/*
* 长连接建立后,需要马上发送PlayerInfo和JoinGame两个数据包,
* 否则ygopro服务端超过2s后会自动断连。
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { store } from "@/store";
import {
player0Update,
player1Update,
observerIncrement,
player0Leave,
player0Update,
player1Leave,
observerIncrement,
player1Update,
} from "@/reducers/playerSlice";
import { store } from "@/store";
const READY_STATE = "ready";
const NO_READY_STATE = "not ready";
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { store } from "@/store";
import { player0Enter, player1Enter } from "@/reducers/playerSlice";
import { store } from "@/store";
export default function handleHsPlayerEnter(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { store } from "@/store";
import { setJoined } from "@/reducers/joinSlice";
import { store } from "@/store";
export default function handleJoinGame(pb: ygopro.YgoStocMsg) {
const dispatch = store.dispatch;
......
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import {
hostChange,
player0Update,
player1Update,
hostChange,
updateIsHost,
} from "@/reducers/playerSlice";
import { store } from "@/store";
......
......@@ -2,11 +2,12 @@
* 全局状态存储模块
* */
import { configureStore, Unsubscribe } from "@reduxjs/toolkit";
import joinedReducer from "./reducers/joinSlice";
import chatReducer from "./reducers/chatSlice";
import playerReducer from "./reducers/playerSlice";
import moraReducer from "./reducers/moraSlice";
import duelReducer from "./reducers/duel/mod";
import joinedReducer from "./reducers/joinSlice";
import moraReducer from "./reducers/moraSlice";
import playerReducer from "./reducers/playerSlice";
export const store = configureStore({
reducer: {
......
import { Layout } from "antd";
import React from "react";
import { useConfig } from "@/config";
const NeosConfig = useConfig();
const layoutConfig = NeosConfig.ui.layout;
const { Header, Footer, Sider, Content } = Layout;
const headerStyle: React.CSSProperties = {
textAlign: "center",
alignContent: "center",
color: "#fff",
height: layoutConfig.header.height,
};
const contentStyle: React.CSSProperties = {
textAlign: "center",
minHeight: 120,
height: layoutConfig.content.height,
lineHeight: "120px",
paddingLeft: `${layoutConfig.sider.width}px`,
};
const siderStyle: React.CSSProperties = {
lineHeight: "120px",
position: "fixed",
overflow: "auto",
height: "100vh",
padding: "50px 20px",
color: "#fff",
};
const footerStyle: React.CSSProperties = {
textAlign: "center",
height: layoutConfig.footer.height,
color: "#fff",
paddingLeft: `${layoutConfig.sider.width}px`,
};
const NeosLayout = (props: {
sider: React.ReactNode;
header: React.ReactNode;
content: React.ReactNode;
footer: React.ReactNode;
}) => {
return (
<Layout hasSider>
<Sider style={siderStyle} width={layoutConfig.sider.width}>
{props.sider}
</Sider>
<Layout>
<Header style={headerStyle}>{props.header}</Header>
<Content style={contentStyle}>{props.content}</Content>
<Footer style={footerStyle}>{props.footer}</Footer>
</Layout>
</Layout>
);
};
export default NeosLayout;
import * as BABYLON from "@babylonjs/core";
import { Row } from "antd";
import React from "react";
import { Engine, Scene } from "react-babylonjs";
import { Provider, ReactReduxContext } from "react-redux";
import { useConfig } from "@/config";
import NeosLayout from "./Layout";
import {
Alert,
CardListModal,
CardModal,
CheckCardModal,
CheckCardModalV2,
CheckCardModalV3,
CheckCounterModal,
DuelTimeLine,
HintNotification,
OptionModal,
Phase,
PlayerStatus,
PositionModal,
SendBox,
SortCardModal,
YesNoModal,
} from "./Message";
import {
ExtraDeck,
Field,
Graveyard,
Hands,
Magics,
Monsters,
} from "./PlayMat";
import { BanishedZone } from "./PlayMat/BanishedZone";
import { CommonDeck } from "./PlayMat/Deck";
const NeosConfig = useConfig();
// Ref: https://github.com/brianzinn/react-babylonjs/issues/126
const NeosDuel = () => {
return (
<>
<Alert />
<NeosLayout
sider={<NeosSider />}
header={<PlayerStatus />}
content={<NeosCanvas />}
footer={<Phase />}
/>
<CardModal />
<CardListModal />
<HintNotification />
<CheckCardModal />
<YesNoModal />
<PositionModal />
<OptionModal />
<CheckCardModalV2 />
<CheckCardModalV3 />
<CheckCounterModal />
<SortCardModal />
</>
);
};
const NeosSider = () => (
<>
<Row>
<DuelTimeLine />
</Row>
<Row>
<SendBox />
</Row>
</>
);
const NeosCanvas = () => (
<ReactReduxContext.Consumer>
{({ store }) => (
<Engine antialias adaptToDeviceRatio canvasId="babylonJS">
<Scene>
<Provider store={store}>
<Camera />
<Light />
<Hands />
<Monsters />
<Magics />
<Field />
<CommonDeck />
<ExtraDeck />
<Graveyard />
<BanishedZone />
<Field />
<Ground />
</Provider>
</Scene>
</Engine>
)}
</ReactReduxContext.Consumer>
);
const Camera = () => (
<freeCamera
name="duel-camera"
position={new BABYLON.Vector3(0, 8, -10)}
target={BABYLON.Vector3.Zero()}
></freeCamera>
);
const Light = () => (
<hemisphericLight
name="duel-light"
direction={new BABYLON.Vector3(1, 2.5, 1)}
intensity={0.7}
></hemisphericLight>
);
const Ground = () => {
const shape = NeosConfig.ui.ground;
const texture = new BABYLON.Texture(`${NeosConfig.assetsPath}/newfield.png`);
texture.hasAlpha = true;
return (
<ground name="duel-ground" width={shape.width} height={shape.height}>
<standardMaterial
name="duel-ground-mat"
diffuseTexture={texture}
></standardMaterial>
</ground>
);
};
export default NeosDuel;
import { Alert as AntdAlert } from "antd";
import React from "react";
import { useNavigate } from "react-router-dom";
import { sendSurrender } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import { selectUnimplemented } from "@/reducers/duel/mod";
export const Alert = () => {
const unimplemented = useAppSelector(selectUnimplemented);
const navigate = useNavigate();
return (
<>
{unimplemented ? (
<AntdAlert
message={`Unimplemented message with code=${unimplemented}`}
description="It seems that there's something unimplemented by Neos. Sincerely apologize for that. Contact use to fix this issue: <ccc@neos.moe>"
showIcon
type="error"
closable
banner
afterClose={() => {
// 发送投降信号
sendSurrender();
navigate("/");
}}
/>
) : (
<></>
)}
</>
);
};
import { Button, Drawer, List } from "antd";
import React from "react";
import { sendSelectIdleCmdResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
clearAllIdleInteractivities,
setCardListModalIsOpen,
} from "@/reducers/duel/mod";
import {
selectCardListModalInfo,
selectCardListModalIsOpen,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
const NeosConfig = useConfig();
const CARD_WIDTH = 100;
export const CardListModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectCardListModalIsOpen);
const list = useAppSelector(selectCardListModalInfo);
const handleOkOrCancel = () => {
dispatch(setCardListModalIsOpen(false));
};
return (
<Drawer open={isOpen} onClose={handleOkOrCancel}>
<List
itemLayout="horizontal"
dataSource={list}
renderItem={(item) => (
<List.Item
actions={item.interactivies.map((interactivy, idx) => (
<Button
key={idx}
onClick={() => {
sendSelectIdleCmdResponse(interactivy.response);
dispatch(setCardListModalIsOpen(false));
dispatch(clearAllIdleInteractivities(0));
dispatch(clearAllIdleInteractivities(1));
}}
>
{interactivy.desc}
</Button>
))}
extra={
<img
alt={item.meta?.text.name}
src={`${NeosConfig.cardImgUrl}/${item.meta?.id}.jpg`}
style={{ width: CARD_WIDTH }}
/>
}
>
<List.Item.Meta
title={item.meta?.text.name}
description={item.meta?.text.desc}
/>
</List.Item>
)}
></List>
</Drawer>
);
};
import Icon, { StarOutlined } from "@ant-design/icons";
import { Button, Card, Col, Modal, Row } from "antd";
import React from "react";
import { sendSelectIdleCmdResponse } from "@/api/ocgcore/ocgHelper";
import { fetchStrings } from "@/api/strings";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
clearAllIdleInteractivities,
setCardModalIsOpen,
} from "@/reducers/duel/mod";
import {
selectCardModalCounters,
selectCardModalInteractivies,
selectCardModalIsOpen,
selectCardModalMeta,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import { ReactComponent as BattleSvg } from "neos-assets/battle-axe.svg";
import { ReactComponent as DefenceSvg } from "neos-assets/checked-shield.svg";
import {
Attribute2StringCodeMap,
extraCardTypes,
Race2StringCodeMap,
Type2StringCodeMap,
} from "../../../common";
const NeosConfig = useConfig();
const { Meta } = Card;
const CARD_WIDTH = 240;
export const CardModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectCardModalIsOpen);
const meta = useAppSelector(selectCardModalMeta);
const name = meta?.text.name;
const types = meta?.data.type;
const race = meta?.data.race;
const attribute = meta?.data.attribute;
const level = meta?.data.level;
const desc = meta?.text.desc;
const atk = meta?.data.atk;
const def = meta?.data.def;
const counters = useAppSelector(selectCardModalCounters);
const imgUrl = meta?.id
? `${NeosConfig.cardImgUrl}/${meta.id}.jpg`
: undefined;
const interactivies = useAppSelector(selectCardModalInteractivies);
const handleOkOrCancel = () => {
dispatch(setCardModalIsOpen(false));
};
return (
<Modal open={isOpen} onOk={handleOkOrCancel} onCancel={handleOkOrCancel}>
<Card
hoverable
style={{ width: CARD_WIDTH }}
cover={<img alt={name} src={imgUrl} />}
>
<Meta title={name} />
<AttLine
types={extraCardTypes(types || 0)}
race={race}
attribute={attribute}
/>
<AtkLine level={level} atk={atk} def={def} />
<CounterLine counters={counters} />
<p>{desc}</p>
</Card>
{interactivies.map((interactive, idx) => {
return (
<Button
key={idx}
onClick={() => {
sendSelectIdleCmdResponse(interactive.response);
dispatch(setCardModalIsOpen(false));
dispatch(clearAllIdleInteractivities(0));
dispatch(clearAllIdleInteractivities(1));
}}
>
{interactive.desc}
</Button>
);
})}
</Modal>
);
};
const AtkLine = (props: { level?: number; atk?: number; def?: number }) => (
<Row gutter={8}>
{props.level ? (
<Col>
<StarOutlined />
{props.level}
</Col>
) : (
<></>
)}
{props.atk ? (
<Col>
<Icon component={BattleSvg} />
<a>{props.atk}</a>
</Col>
) : (
<></>
)}
<Col>/</Col>
{props.def ? (
<Col>
<Icon component={DefenceSvg} />
<a>{props.def}</a>
</Col>
) : (
<></>
)}
</Row>
);
const AttLine = (props: {
types: number[];
race?: number;
attribute?: number;
}) => {
const race = props.race
? fetchStrings("!system", Race2StringCodeMap.get(props.race) || 0)
: undefined;
const attribute = props.attribute
? fetchStrings("!system", Attribute2StringCodeMap.get(props.attribute) || 0)
: undefined;
const types = props.types
.map((t) => fetchStrings("!system", Type2StringCodeMap.get(t) || 0))
.join("|");
return (
<Row gutter={8}>
<Col>{`[${types}]`}</Col>
{race ? <Col>{race}</Col> : <></>}
<Col>/</Col>
{attribute ? <Col>{attribute}</Col> : <></>}
</Row>
);
};
const CounterLine = (props: { counters: { [type: number]: number } }) => {
const counters = [];
for (const counterType in props.counters) {
const count = props.counters[counterType];
if (count > 0) {
const counterStr = fetchStrings("!counter", `0x${counterType}`);
counters.push(`${counterStr}: ${count}`);
}
}
return counters.length > 0 ? (
<Row gutter={8}>
{counters.map((counter) => (
<Col>{counter}</Col>
))}
</Row>
) : (
<></>
);
};
import { ThunderboltOutlined } from "@ant-design/icons";
import { CheckCard, CheckCardProps } from "@ant-design/pro-components";
import { Button, Col, Popover, Row } from "antd";
import React, { useState } from "react";
import {
sendSelectCardResponse,
sendSelectChainResponse,
} from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import {
resetCheckCardModal,
setCheckCardModalIsOpen,
} from "@/reducers/duel/mod";
import {
selectCheckCardModalCacnelResponse,
selectCheckCardModalCancelAble,
selectCheckCardModalIsOpen,
selectCheckCardModalMinMax,
selectCheckCardModalOnSubmit,
selectCheckCardModalTags,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import { DragModal } from "./DragModal";
const NeosConfig = useConfig();
export const CheckCardModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectCheckCardModalIsOpen);
const { min, max } = useAppSelector(selectCheckCardModalMinMax);
const tabs = useAppSelector(selectCheckCardModalTags);
const onSubmit = useAppSelector(selectCheckCardModalOnSubmit);
const cancelAble = useAppSelector(selectCheckCardModalCancelAble);
const cancelResponse = useAppSelector(selectCheckCardModalCacnelResponse);
const [response, setResponse] = useState<number[]>([]);
const defaultValue: number[] = [];
const hint = useAppSelector(selectHint);
const preHintMsg = hint?.esHint || "";
const selectHintMsg = hint?.esSelectHint || "请选择卡片";
// TODO: 这里可以考虑更好地封装
const sendResponseHandler = (
handlerName: string | undefined,
response: number[]
) => {
switch (handlerName) {
case "sendSelectChainResponse": {
sendSelectChainResponse(response[0]);
break;
}
case "sendSelectCardResponse": {
sendSelectCardResponse(response);
break;
}
default: {
}
}
};
return (
<DragModal
title={`${preHintMsg} ${selectHintMsg} ${min}-${max}`}
open={isOpen}
closable={false}
footer={
<>
<Button
disabled={response.length < min || response.length > max}
onClick={() => {
sendResponseHandler(onSubmit, response);
dispatch(setCheckCardModalIsOpen(false));
dispatch(resetCheckCardModal());
}}
onFocus={() => {}}
onBlur={() => {}}
>
submit
</Button>
{cancelAble ? (
<Button
onClick={() => {
if (cancelResponse) {
sendResponseHandler(onSubmit, [cancelResponse]);
}
dispatch(setCheckCardModalIsOpen(false));
dispatch(resetCheckCardModal());
}}
onFocus={() => {}}
onBlur={() => {}}
>
cancel
</Button>
) : (
<></>
)}
</>
}
width={800}
>
<CheckCard.Group
multiple
bordered
size="small"
defaultValue={defaultValue}
onChange={(value) => {
// @ts-ignore
setResponse(value);
}}
>
{tabs.map((tab, idx) => {
return (
<Row key={idx}>
{tab.options.map((option, idx) => {
return (
<Col span={4} key={idx}>
<HoverCheckCard
hoverContent={option.effectDesc}
title={option.meta.text.name}
description={option.meta.text.desc}
style={{ width: 120 }}
cover={
<img
alt={option.meta.id.toString()}
src={
option.meta.id
? `${NeosConfig.cardImgUrl}/${option.meta.id}.jpg`
: `${NeosConfig.assetsPath}/card_back.jpg`
}
style={{ width: 100 }}
/>
}
value={option.response}
/>
</Col>
);
})}
</Row>
);
})}
</CheckCard.Group>
</DragModal>
);
};
const HoverCheckCard = (props: CheckCardProps & { hoverContent?: string }) => {
const [hover, setHover] = useState(false);
const onMouseEnter = () => setHover(true);
const onMouseLeave = () => setHover(false);
return (
<>
<CheckCard {...props} />
{props.hoverContent ? (
<Popover content={<p>{props.hoverContent}</p>} open={hover}>
<Button
icon={<ThunderboltOutlined />}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
></Button>
</Popover>
) : (
<></>
)}
</>
);
};
import { CheckCard } from "@ant-design/pro-components";
import { Button, Card, Col, Row } from "antd";
import React from "react";
import { sendSelectUnselectCardResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import {
resetCheckCardModalV2,
setCheckCardModalV2IsOpen,
setCheckCardModalV2ResponseAble,
} from "@/reducers/duel/mod";
import {
selectCheckCardModalV2CancelAble,
selectCheckCardModalV2FinishAble,
selectCheckCardModalV2IsOpen,
selectCheckCardModalV2MinMax,
selectCheckCardModalV2ResponseAble,
selectCheckCardModalV2SelectAbleOptions,
selectCheckCardModalV2SelectedOptions,
} from "@/reducers/duel/modal/checkCardModalV2Slice";
import { store } from "@/store";
import { DragModal } from "./DragModal";
const NeosConfig = useConfig();
export const CheckCardModalV2 = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectCheckCardModalV2IsOpen);
const { min, max } = useAppSelector(selectCheckCardModalV2MinMax);
const cancelable = useAppSelector(selectCheckCardModalV2CancelAble);
const finishable = useAppSelector(selectCheckCardModalV2FinishAble);
const selectableOptions = useAppSelector(
selectCheckCardModalV2SelectAbleOptions
);
const selectedOptions = useAppSelector(selectCheckCardModalV2SelectedOptions);
const responseable = useAppSelector(selectCheckCardModalV2ResponseAble);
const hint = useAppSelector(selectHint);
const preHintMsg = hint?.esHint || "";
const selectHintMsg = hint?.esSelectHint || "请选择卡片";
const onFinishOrCancel = () => {
sendSelectUnselectCardResponse({ cancel_or_finish: true });
dispatch(setCheckCardModalV2IsOpen(false));
dispatch(resetCheckCardModalV2());
dispatch(setCheckCardModalV2ResponseAble(false));
};
return (
<DragModal
title={`${preHintMsg} ${selectHintMsg} ${min}-${max}`}
open={isOpen}
closable={false}
footer={
<>
<Button
disabled={!finishable || !responseable}
onClick={onFinishOrCancel}
>
finish
</Button>
<Button
disabled={!cancelable || !responseable}
onClick={onFinishOrCancel}
>
cancel
</Button>
</>
}
width={800}
>
<CheckCard.Group
bordered
size="small"
onChange={(value) => {
if (responseable) {
dispatch(setCheckCardModalV2IsOpen(false));
// @ts-ignore
sendSelectUnselectCardResponse({ selected_ptr: value });
dispatch(setCheckCardModalV2ResponseAble(false));
}
}}
>
<Row>
{selectableOptions.map((option, idx) => {
return (
<Col span={4} key={idx}>
<CheckCard
title={option.name}
description={option.desc}
style={{ width: 120 }}
cover={
<img
alt={option.code.toString()}
src={`${NeosConfig.cardImgUrl}/${option.code}.jpg`}
style={{ width: 100 }}
/>
}
value={option.response}
/>
</Col>
);
})}
</Row>
</CheckCard.Group>
<p>已经选择的卡片</p>
<Row>
{selectedOptions.map((option, idx) => {
return (
<Col span={4} key={idx}>
<Card
hoverable
style={{ width: 120 }}
cover={
<img
alt={option.code.toString()}
src={`${NeosConfig.cardImgUrl}/${option.code}.jpg`}
/>
}
/>
</Col>
);
})}
</Row>
</DragModal>
);
};
import { CheckCard } from "@ant-design/pro-components";
import { Button, Card, Col, Row } from "antd";
import React, { useState } from "react";
import { sendSelectCardResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import {
resetCheckCardModalV3,
setCheckCardModalV3IsOpen,
setCheckCardModalV3ResponseAble,
} from "@/reducers/duel/mod";
import { selectCheckCardModalV3 } from "@/reducers/duel/modal/checkCardModalV3Slice";
import { store } from "@/store";
import { DragModal } from "./DragModal";
const NeosConfig = useConfig();
export const CheckCardModalV3 = () => {
const dispatch = store.dispatch;
const state = useAppSelector(selectCheckCardModalV3);
const isOpen = state.isOpen;
const min = state.selectMin || 0;
const max = state.selectMax || 0;
const mustSelectOptions = state.mustSelectList;
const selectAbleOptions = state.selectAbleList;
const [selectedOptions, setSelectedOptions] = useState([]);
const overflow = state.overflow;
const LevelSum = state.allLevel;
const Level1Sum = mustSelectOptions
.concat(selectedOptions)
.map((option) => option.level1)
.reduce((sum, current) => sum + current, 0);
const Level2Sum = mustSelectOptions
.concat(selectedOptions)
.map((option) => option.level2)
.reduce((sum, current) => sum + current, 0);
const hint = useAppSelector(selectHint);
const preHintMsg = hint?.esHint || "";
const selectHintMsg = hint?.esSelectHint || "请选择卡片";
const responseable =
(overflow
? Level1Sum >= LevelSum || Level2Sum >= LevelSum
: Level1Sum == LevelSum || Level2Sum == LevelSum) &&
selectedOptions.length <= max &&
selectedOptions.length >= min;
const onFinish = () => {
sendSelectCardResponse(
mustSelectOptions.concat(selectedOptions).map((option) => option.response)
);
dispatch(setCheckCardModalV3IsOpen(false));
dispatch(resetCheckCardModalV3());
dispatch(setCheckCardModalV3ResponseAble(false));
};
return (
<DragModal
title={`${preHintMsg} ${selectHintMsg} ${min}-${max}`}
open={isOpen}
closable={false}
footer={
<>
<Button disabled={!responseable} onClick={onFinish}>
finish
</Button>
</>
}
width={800}
>
<CheckCard.Group
bordered
size="small"
multiple={true}
onChange={(values: any) => {
console.log(values);
setSelectedOptions(values);
}}
>
<Row>
{selectAbleOptions.map((option, idx) => {
return (
<Col span={4} key={idx}>
<CheckCard
title={option.meta.text.name}
description={option.meta.text.desc}
style={{ width: 120 }}
cover={
<img
alt={option.meta.id.toString()}
src={`${NeosConfig.cardImgUrl}/${option.meta.id}.jpg`}
style={{ width: 100 }}
/>
}
value={option}
/>
</Col>
);
})}
</Row>
</CheckCard.Group>
<p>必须选择的卡片</p>
<Row>
{mustSelectOptions.map((option, idx) => {
return (
<Col span={4} key={idx}>
<Card
hoverable
style={{ width: 120 }}
cover={
<img
alt={option.meta.id.toString()}
src={`${NeosConfig.cardImgUrl}/${option.meta.id}.jpg`}
/>
}
/>
</Col>
);
})}
</Row>
</DragModal>
);
};
import { Button, Card, Col, InputNumber, Row } from "antd";
import React, { useState } from "react";
import { sendSelectCounterResponse } from "@/api/ocgcore/ocgHelper";
import { fetchStrings } from "@/api/strings";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { clearCheckCounter } from "@/reducers/duel/mod";
import { selectCheckCounterModal } from "@/reducers/duel/modal/checkCounterModalSlice";
import { store } from "@/store";
import { DragModal } from "./DragModal";
const NeosConfig = useConfig();
export const CheckCounterModal = () => {
const dispatch = store.dispatch;
const state = useAppSelector(selectCheckCounterModal);
const isOpen = state.isOpen;
const counterName = fetchStrings("!counter", `0x${state.counterType!}`);
const min = state.min || 0;
const options = state.options;
const [selected, setSelected] = useState(new Array(options.length));
const sum = selected.reduce((sum, current) => sum + current, 0);
const finishable = sum == min;
const onFinish = () => {
sendSelectCounterResponse(selected);
dispatch(clearCheckCounter());
};
return (
<DragModal
title={`请移除${min}个${counterName}`}
open={isOpen}
closable={false}
footer={
<Button disabled={!finishable} onClick={onFinish}>
finish
</Button>
}
>
<Row>
{options.map((option, idx) => {
return (
<Col span={4} key={idx}>
<Card
hoverable
style={{ width: 120 }}
cover={
<img
alt={option.code.toString()}
src={`${NeosConfig.cardImgUrl}/${option.code}.jpg`}
/>
}
>
<InputNumber
min={0}
max={option.max}
defaultValue={0}
onChange={(value) => {
let newSelected = [...selected];
newSelected[idx] = value || 0;
setSelected(newSelected);
}}
/>
</Card>
</Col>
);
})}
</Row>
</DragModal>
);
};
// 经过封装的可拖拽`Modal`
import { Modal, ModalProps } from "antd";
import React, { useRef, useState } from "react";
import type { DraggableData, DraggableEvent } from "react-draggable";
import Draggable from "react-draggable";
export interface DragModalProps extends ModalProps {}
export const DragModal = (props: DragModalProps) => {
const dragRef = useRef<HTMLDivElement>(null);
const [bounds, setBounds] = useState({
left: 0,
top: 0,
bottom: 0,
right: 0,
});
const onStart = (_event: DraggableEvent, uiData: DraggableData) => {
const { clientWidth, clientHeight } = window.document.documentElement;
const targetRect = dragRef.current?.getBoundingClientRect();
if (!targetRect) {
return;
}
setBounds({
left: -targetRect.left + uiData.x,
right: clientWidth - (targetRect.right - uiData.x),
top: -targetRect.top + uiData.y,
bottom: clientHeight - (targetRect.bottom - uiData.y),
});
};
return (
<Modal
{...props}
modalRender={(modal) => (
<Draggable bounds={bounds} onStart={onStart}>
<div ref={dragRef}>{modal}</div>
</Draggable>
)}
>
{props.children}
</Modal>
);
};
import { notification } from "antd";
import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import { selectDuelResult, selectWaiting } from "@/reducers/duel/mod";
import { selectCurrentPhase } from "@/reducers/duel/phaseSlice";
import MsgWin = ygopro.StocGameMessage.MsgWin;
import { useConfig } from "@/config";
const NeosConfig = useConfig();
export const HintNotification = () => {
const hint = useAppSelector(selectHint);
const currentPhase = useAppSelector(selectCurrentPhase);
const waiting = useAppSelector(selectWaiting);
const result = useAppSelector(selectDuelResult);
const navigate = useNavigate();
const [api, contextHolder] = notification.useNotification({
maxCount: NeosConfig.ui.hint.maxCount,
});
useEffect(() => {
if (hint && hint.msg) {
api.info({
message: `${hint.msg}`,
placement: "bottom",
});
}
}, [hint?.msg]);
useEffect(() => {
if (currentPhase) {
api.info({
message: `<当前阶段>${currentPhase}`,
placement: "topRight",
});
}
}, [currentPhase]);
useEffect(() => {
if (waiting) {
api.info({
message: "...等待对方行动中...",
placement: "top",
duration: NeosConfig.ui.hint.waitingDuration,
});
}
}, [waiting]);
useEffect(() => {
if (result) {
const message =
result == MsgWin.ActionType.Win
? "胜利"
: MsgWin.ActionType.Defeated
? "失败"
: "未知结果";
api.info({
message,
placement: "bottom",
onClose() {
navigate("/");
},
});
}
}, [result]);
return <>{contextHolder}</>;
};
import { CheckCard } from "@ant-design/pro-components";
import { Button } from "antd";
import React, { useState } from "react";
import { sendSelectOptionResponse } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import { resetOptionModal, setOptionModalIsOpen } from "@/reducers/duel/mod";
import {
selectOptionModalIsOpen,
selectOptionModalOptions,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import { DragModal } from "./DragModal";
export const OptionModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectOptionModalIsOpen);
const options = useAppSelector(selectOptionModalOptions);
const [selected, setSelected] = useState<number | undefined>(undefined);
return (
<DragModal
title="请选择需要发动的效果"
open={isOpen}
closable={false}
footer={
<Button
disabled={selected === undefined}
onClick={() => {
if (selected !== undefined) {
sendSelectOptionResponse(selected);
dispatch(setOptionModalIsOpen(false));
dispatch(resetOptionModal());
}
}}
>
submit
</Button>
}
>
<CheckCard.Group
bordered
size="small"
onChange={(value) => {
// @ts-ignore
setSelected(value);
}}
>
{options.map((option, idx) => (
<CheckCard key={idx} title={option.msg} value={option.response} />
))}
</CheckCard.Group>
</DragModal>
);
};
import Icon from "@ant-design/icons";
import { Button, Modal, Space } from "antd";
import React, { useState } from "react";
import {
sendSelectBattleCmdResponse,
sendSelectIdleCmdResponse,
sendSurrender,
} from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import {
clearAllIdleInteractivities,
setEnableBp,
setEnableEp,
setEnableM2,
} from "@/reducers/duel/mod";
import {
selectCurrentPhase,
selectEnableBp,
selectEnableEp,
selectEnableM2,
} from "@/reducers/duel/phaseSlice";
import { store } from "@/store";
import { ReactComponent as BattleSvg } from "neos-assets/crossed-swords.svg";
import { ReactComponent as EpSvg } from "neos-assets/power-button.svg";
import { ReactComponent as Main2Svg } from "neos-assets/sword-in-stone.svg";
import { ReactComponent as SurrenderSvg } from "neos-assets/truce.svg";
const IconSize = "150%";
const SpaceSize = 16;
const PhaseButton = (props: {
text: string;
enable: boolean;
onClick: () => void;
icon?: React.ReactNode;
}) => {
return (
<Button
icon={props.icon}
disabled={!props.enable}
onClick={props.onClick}
size="large"
>
{props.text}
</Button>
);
};
export const Phase = () => {
const dispatch = store.dispatch;
const enableBp = useAppSelector(selectEnableBp);
const enableM2 = useAppSelector(selectEnableM2);
const enableEp = useAppSelector(selectEnableEp);
const currentPhase = useAppSelector(selectCurrentPhase);
const [modalOpen, setModalOpen] = useState(false);
const response =
currentPhase === "BATTLE_START" ||
currentPhase === "BATTLE_STEP" ||
currentPhase === "DAMAGE" ||
currentPhase === "DAMAGE_GAL" ||
currentPhase === "BATTLE"
? 3
: 7;
const onBp = () => {
dispatch(clearAllIdleInteractivities(0));
dispatch(clearAllIdleInteractivities(0));
sendSelectIdleCmdResponse(6);
dispatch(setEnableBp(false));
};
const onM2 = () => {
dispatch(clearAllIdleInteractivities(0));
dispatch(clearAllIdleInteractivities(0));
sendSelectBattleCmdResponse(2);
dispatch(setEnableM2(false));
};
const onEp = () => {
dispatch(clearAllIdleInteractivities(0));
dispatch(clearAllIdleInteractivities(0));
sendSelectIdleCmdResponse(response);
dispatch(setEnableEp(false));
};
const onSurrender = () => {
setModalOpen(true);
};
return (
<Space wrap size={SpaceSize}>
<PhaseButton
icon={<Icon component={BattleSvg} style={{ fontSize: IconSize }} />}
enable={enableBp}
text="战斗阶段"
onClick={onBp}
/>
<PhaseButton
icon={<Icon component={Main2Svg} style={{ fontSize: IconSize }} />}
enable={enableM2}
text="主要阶段2"
onClick={onM2}
/>
<PhaseButton
icon={<Icon component={EpSvg} style={{ fontSize: IconSize }} />}
enable={enableEp}
text="结束回合"
onClick={onEp}
/>
<PhaseButton
icon={<Icon component={SurrenderSvg} style={{ fontSize: IconSize }} />}
enable={true}
text="投降"
onClick={onSurrender}
/>
<Modal
title="是否确认要投降?"
open={modalOpen}
closable={false}
footer={
<>
<Button
onClick={() => {
sendSurrender();
setModalOpen(false);
}}
>
Yes
</Button>
<Button
onClick={() => {
setModalOpen(false);
}}
>
No
</Button>
</>
}
/>
</Space>
);
};
import { CheckCard } from "@ant-design/pro-components";
import { Button } from "antd";
import React, { useState } from "react";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { sendSelectPositionResponse } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import {
resetPositionModal,
setPositionModalIsOpen,
} from "@/reducers/duel/mod";
import {
selectPositionModalIsOpen,
selectPositionModalPositions,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import { DragModal } from "./DragModal";
export const PositionModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectPositionModalIsOpen);
const positions = useAppSelector(selectPositionModalPositions);
const [selected, setSelected] = useState<ygopro.CardPosition | undefined>(
undefined
);
return (
<DragModal
title="请选择表示形式"
open={isOpen}
closable={false}
footer={
<Button
disabled={selected === undefined}
onClick={() => {
if (selected !== undefined) {
sendSelectPositionResponse(selected);
dispatch(setPositionModalIsOpen(false));
dispatch(resetPositionModal());
}
}}
>
submit
</Button>
}
>
<CheckCard.Group
bordered
size="small"
onChange={(value) => {
// @ts-ignore
setSelected(value);
}}
>
{positions.map((position, idx) => (
<CheckCard
key={idx}
title={cardPositionToChinese(position)}
value={position}
/>
))}
</CheckCard.Group>
</DragModal>
);
};
function cardPositionToChinese(position: ygopro.CardPosition): string {
switch (position) {
case ygopro.CardPosition.FACEUP_ATTACK: {
return "正面攻击形式";
}
case ygopro.CardPosition.FACEUP_DEFENSE: {
return "正面防守形式";
}
case ygopro.CardPosition.FACEDOWN_ATTACK: {
return "背面攻击形式";
}
case ygopro.CardPosition.FACEDOWN_DEFENSE: {
return "背面防守形式";
}
default: {
return "[?]";
}
}
}
import { SendOutlined } from "@ant-design/icons";
import { Button, Col, Input, Row } from "antd";
import React, { useState } from "react";
import { sendChat } from "@/api/ocgcore/ocgHelper";
export const SendBox = () => {
const [content, setContent] = useState("");
return (
<>
<Row>
<Input.TextArea
placeholder="Message to sent..."
autoSize={{ minRows: 3, maxRows: 4 }}
value={content}
onChange={(e) => {
setContent(e.target.value);
}}
/>
</Row>
<Row>
<Col>
<Button
icon={<SendOutlined />}
onClick={() => {
sendChat(content);
setContent("");
}}
disabled={!content}
/>
</Col>
</Row>
</>
);
};
import {
closestCenter,
DndContext,
DragEndEvent,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
} from "@dnd-kit/core";
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
useSortable,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Button, Card, Modal } from "antd";
import React, { useEffect, useState } from "react";
import { CardMeta } from "@/api/cards";
import { sendSortCardResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { resetSortCardModal } from "@/reducers/duel/mod";
import { selectSortCardModal } from "@/reducers/duel/modal/sortCardModalSlice";
import { store } from "@/store";
const NeosConfig = useConfig();
export const SortCardModal = () => {
const dispatch = store.dispatch;
const state = useAppSelector(selectSortCardModal);
const isOpen = state.isOpen;
const options = state.options;
const [items, setItems] = useState(options);
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
);
const onFinish = () => {
sendSortCardResponse(items.map((item) => item.response));
dispatch(resetSortCardModal());
};
const onDragEnd = (event: DragEndEvent) => {
const { active, over } = event;
if (active.id !== over?.id) {
setItems((items) => {
const oldIndex = items.findIndex((item) => item.response == active.id);
const newIndex = items.findIndex((item) => item.response === over?.id);
return arrayMove(items, oldIndex, newIndex);
});
}
};
useEffect(() => {
setItems(options);
}, [options]);
return (
<Modal
title="请为下列卡牌排序"
open={isOpen}
closable={false}
footer={<Button onClick={onFinish}>finish</Button>}
>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={onDragEnd}
>
<SortableContext
items={items.map((item) => item.response)}
strategy={verticalListSortingStrategy}
>
{items.map((item) => (
<SortableItem
key={item.response}
id={item.response}
meta={item.meta}
/>
))}
</SortableContext>
</DndContext>
</Modal>
);
};
const SortableItem = (props: { id: number; meta: CardMeta }) => {
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id: props.id });
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<div ref={setNodeRef} style={style} {...attributes} {...listeners}>
<Card
style={{ width: 100 }}
cover={
<img
alt={props.meta.id.toString()}
src={`${NeosConfig.cardImgUrl}/${props.meta.id}.jpg`}
/>
}
/>
</div>
);
};
import { UserOutlined } from "@ant-design/icons";
import { CheckCard } from "@ant-design/pro-components";
import { Avatar } from "antd";
import React from "react";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
selectMeInitInfo,
selectOpInitInfo,
} from "@/reducers/duel/initInfoSlice";
import { selectWaiting } from "@/reducers/duel/mod";
const NeosConfig = useConfig();
const Config = NeosConfig.ui.status;
const avatarSize = 40;
const ME_VALUE = "myself";
const OP_VALUE = "opponent";
export const PlayerStatus = () => {
const meInfo = useAppSelector(selectMeInitInfo);
const opInfo = useAppSelector(selectOpInitInfo);
const waiting = useAppSelector(selectWaiting) || false;
return (
<CheckCard.Group
bordered
style={{ height: `${NeosConfig.ui.layout.header.height}` }}
value={waiting ? OP_VALUE : ME_VALUE}
>
<CheckCard
avatar={
<Avatar
size={avatarSize}
style={{ backgroundColor: Config.opAvatarColor }}
icon={<UserOutlined />}
/>
}
title={OP_VALUE}
description={`Lp: ${opInfo?.life || 0}`}
value={OP_VALUE}
style={{
position: "absolute",
left: `${NeosConfig.ui.layout.sider.width}px`,
}}
/>
<CheckCard
avatar={
<Avatar
size={avatarSize}
style={{ backgroundColor: Config.meAvatarColor }}
icon={<UserOutlined />}
/>
}
title={ME_VALUE}
description={`Lp: ${meInfo?.life || 0}`}
value={ME_VALUE}
style={{
position: "absolute",
right: "0px",
}}
/>
</CheckCard.Group>
);
};
import { MessageOutlined } from "@ant-design/icons";
import { Timeline, TimelineItemProps } from "antd";
import React, { useEffect, useState } from "react";
import { useAppSelector } from "@/hook";
import { selectChat } from "@/reducers/chatSlice";
export const DuelTimeLine = () => {
const [items, setItems] = useState<TimelineItemProps[]>([]);
const chat = useAppSelector(selectChat);
useEffect(() => {
setItems((prev) =>
prev.concat([
{
dot: <MessageOutlined />,
children: chat,
color: "green",
},
])
);
}, [chat]);
return <Timeline items={items} />;
};
import { Button } from "antd";
import React from "react";
import { sendSelectEffectYnResponse } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import { setYesNoModalIsOpen } from "@/reducers/duel/mod";
import {
selectYesNoModalIsOpen,
selectYesNOModalMsg,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import { DragModal } from "./DragModal";
export const YesNoModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectYesNoModalIsOpen);
const msg = useAppSelector(selectYesNOModalMsg);
const hint = useAppSelector(selectHint);
const preHintMsg = hint?.esHint || "";
return (
<DragModal
title={`${preHintMsg} ${msg}`}
open={isOpen}
closable={false}
footer={
<>
<Button
onClick={() => {
sendSelectEffectYnResponse(true);
dispatch(setYesNoModalIsOpen(false));
}}
>
Yes
</Button>
<Button
onClick={() => {
sendSelectEffectYnResponse(false);
dispatch(setYesNoModalIsOpen(false));
}}
>
No
</Button>
</>
}
/>
);
};
import { Alert as AntdAlert } from "antd";
import React from "react";
import { useNavigate } from "react-router-dom";
import { sendSurrender } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import { selectUnimplemented } from "@/reducers/duel/mod";
import { Alert } from "antd";
const NeosAlert = () => {
export const Alert = () => {
const unimplemented = useAppSelector(selectUnimplemented);
const navigate = useNavigate();
return (
<>
{unimplemented ? (
<Alert
<AntdAlert
message={`Unimplemented message with code=${unimplemented}`}
description="It seems that there's something unimplemented by Neos. Sincerely apologize for that. Contact use to fix this issue: <ccc@neos.moe>"
showIcon
......@@ -31,5 +32,3 @@ const NeosAlert = () => {
</>
);
};
export default NeosAlert;
import { Button, Drawer, List } from "antd";
import React from "react";
import { sendSelectIdleCmdResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { store } from "@/store";
import {
selectCardListModalIsOpen,
selectCardListModalInfo,
} from "@/reducers/duel/modal/mod";
import {
clearAllIdleInteractivities,
setCardListModalIsOpen,
} from "@/reducers/duel/mod";
import { Drawer, List, Button } from "antd";
import { sendSelectIdleCmdResponse } from "@/api/ocgcore/ocgHelper";
import NeosConfig from "../../../neos.config.json";
import {
selectCardListModalInfo,
selectCardListModalIsOpen,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
const NeosConfig = useConfig();
const CARD_WIDTH = 100;
const CardListModal = () => {
export const CardListModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectCardListModalIsOpen);
const list = useAppSelector(selectCardListModalInfo);
......@@ -62,5 +65,3 @@ const CardListModal = () => {
</Drawer>
);
};
export default CardListModal;
import Icon, { StarOutlined } from "@ant-design/icons";
import { Button, Card, Col, Modal, Row } from "antd";
import React from "react";
import { sendSelectIdleCmdResponse } from "@/api/ocgcore/ocgHelper";
import { fetchStrings } from "@/api/strings";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { store } from "@/store";
import {
selectCardModalIsOpen,
clearAllIdleInteractivities,
setCardModalIsOpen,
} from "@/reducers/duel/mod";
import {
selectCardModalCounters,
selectCardModalInteractivies,
selectCardModalIsOpen,
selectCardModalMeta,
selectCardModalCounters,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
import { ReactComponent as BattleSvg } from "neos-assets/battle-axe.svg";
import { ReactComponent as DefenceSvg } from "neos-assets/checked-shield.svg";
import {
setCardModalIsOpen,
clearAllIdleInteractivities,
} from "@/reducers/duel/mod";
import { Modal, Card, Button, Row, Col } from "antd";
import { sendSelectIdleCmdResponse } from "@/api/ocgcore/ocgHelper";
import Icon, { StarOutlined } from "@ant-design/icons";
import NeosConfig from "../../../neos.config.json";
import { ReactComponent as BattleSvg } from "../../../neos-assets/battle-axe.svg";
import { ReactComponent as DefenceSvg } from "../../../neos-assets/checked-shield.svg";
import {
extraCardTypes,
Type2StringCodeMap,
Attribute2StringCodeMap,
extraCardTypes,
Race2StringCodeMap,
} from "../../common";
import { fetchStrings } from "@/api/strings";
Type2StringCodeMap,
} from "../../../common";
const NeosConfig = useConfig();
const { Meta } = Card;
const CARD_WIDTH = 240;
const CardModal = () => {
export const CardModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectCardModalIsOpen);
const meta = useAppSelector(selectCardModalMeta);
......@@ -160,5 +163,3 @@ const CounterLine = (props: { counters: { [type: number]: number } }) => {
<></>
);
};
export default CardModal;
import { ThunderboltOutlined } from "@ant-design/icons";
import { CheckCard, CheckCardProps } from "@ant-design/pro-components";
import { Button, Col, Popover, Row } from "antd";
import React, { useState } from "react";
import {
sendSelectCardResponse,
sendSelectChainResponse,
} from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { store } from "@/store";
import { selectHint } from "@/reducers/duel/hintSlice";
import {
resetCheckCardModal,
setCheckCardModalIsOpen,
} from "@/reducers/duel/mod";
import {
selectCheckCardModalCacnelResponse,
selectCheckCardModalCancelAble,
......@@ -9,22 +22,12 @@ import {
selectCheckCardModalOnSubmit,
selectCheckCardModalTags,
} from "@/reducers/duel/modal/mod";
import {
resetCheckCardModal,
setCheckCardModalIsOpen,
} from "@/reducers/duel/mod";
import { Button, Row, Col, Popover } from "antd";
import { CheckCard, CheckCardProps } from "@ant-design/pro-components";
import {
sendSelectCardResponse,
sendSelectChainResponse,
} from "@/api/ocgcore/ocgHelper";
import { ThunderboltOutlined } from "@ant-design/icons";
import NeosConfig from "../../../neos.config.json";
import DragModal from "./dragModal";
import { selectHint } from "@/reducers/duel/hintSlice";
import { store } from "@/store";
import { DragModal } from "./DragModal";
const CheckCardModal = () => {
const NeosConfig = useConfig();
export const CheckCardModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectCheckCardModalIsOpen);
const { min, max } = useAppSelector(selectCheckCardModalMinMax);
......@@ -165,5 +168,3 @@ const HoverCheckCard = (props: CheckCardProps & { hoverContent?: string }) => {
</>
);
};
export default CheckCardModal;
import { CheckCard } from "@ant-design/pro-components";
import { Button, Card, Col, Row } from "antd";
import React from "react";
import { sendSelectUnselectCardResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { store } from "@/store";
import { Button, Card, Row, Col } from "antd";
import { CheckCard } from "@ant-design/pro-components";
import { selectHint } from "@/reducers/duel/hintSlice";
import {
resetCheckCardModalV2,
setCheckCardModalV2IsOpen,
setCheckCardModalV2ResponseAble,
} from "@/reducers/duel/mod";
import {
selectCheckCardModalV2CancelAble,
selectCheckCardModalV2FinishAble,
......@@ -12,17 +20,12 @@ import {
selectCheckCardModalV2SelectAbleOptions,
selectCheckCardModalV2SelectedOptions,
} from "@/reducers/duel/modal/checkCardModalV2Slice";
import { sendSelectUnselectCardResponse } from "@/api/ocgcore/ocgHelper";
import {
resetCheckCardModalV2,
setCheckCardModalV2IsOpen,
setCheckCardModalV2ResponseAble,
} from "@/reducers/duel/mod";
import NeosConfig from "../../../neos.config.json";
import DragModal from "./dragModal";
import { selectHint } from "@/reducers/duel/hintSlice";
import { store } from "@/store";
const CheckCardModalV2 = () => {
import { DragModal } from "./DragModal";
const NeosConfig = useConfig();
export const CheckCardModalV2 = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectCheckCardModalV2IsOpen);
const { min, max } = useAppSelector(selectCheckCardModalV2MinMax);
......@@ -123,5 +126,3 @@ const CheckCardModalV2 = () => {
</DragModal>
);
};
export default CheckCardModalV2;
import React, { useState } from "react";
import { useAppSelector } from "@/hook";
import { store } from "@/store";
import { Button, Card, Row, Col } from "antd";
import { CheckCard } from "@ant-design/pro-components";
import { Button, Card, Col, Row } from "antd";
import React, { useState } from "react";
import { sendSelectCardResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import {
resetCheckCardModalV3,
setCheckCardModalV3IsOpen,
setCheckCardModalV3ResponseAble,
} from "@/reducers/duel/mod";
import NeosConfig from "../../../neos.config.json";
import { selectCheckCardModalV3 } from "@/reducers/duel/modal/checkCardModalV3Slice";
import DragModal from "./dragModal";
import { selectHint } from "@/reducers/duel/hintSlice";
import { store } from "@/store";
const CheckCardModalV3 = () => {
import { DragModal } from "./DragModal";
const NeosConfig = useConfig();
export const CheckCardModalV3 = () => {
const dispatch = store.dispatch;
const state = useAppSelector(selectCheckCardModalV3);
const isOpen = state.isOpen;
......@@ -119,5 +123,3 @@ const CheckCardModalV3 = () => {
</DragModal>
);
};
export default CheckCardModalV3;
import { Button, Row, Col, Card, InputNumber } from "antd";
import { Button, Card, Col, InputNumber, Row } from "antd";
import React, { useState } from "react";
import { sendSelectCounterResponse } from "@/api/ocgcore/ocgHelper";
import { fetchStrings } from "@/api/strings";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { clearCheckCounter } from "@/reducers/duel/mod";
import { selectCheckCounterModal } from "@/reducers/duel/modal/checkCounterModalSlice";
import { store } from "@/store";
import DragModal from "./dragModal";
import NeosConfig from "../../../neos.config.json";
const CheckCounterModal = () => {
import { DragModal } from "./DragModal";
const NeosConfig = useConfig();
export const CheckCounterModal = () => {
const dispatch = store.dispatch;
const state = useAppSelector(selectCheckCounterModal);
const isOpen = state.isOpen;
......@@ -69,5 +72,3 @@ const CheckCounterModal = () => {
</DragModal>
);
};
export default CheckCounterModal;
// 经过封装的可拖拽`Modal`
import { Modal, ModalProps } from "antd";
import React, { useRef, useState } from "react";
import type { DraggableData, DraggableEvent } from "react-draggable";
import Draggable from "react-draggable";
import { Modal, ModalProps } from "antd";
export interface DragModalProps extends ModalProps {}
const DragModal = (props: DragModalProps) => {
export const DragModal = (props: DragModalProps) => {
const dragRef = useRef<HTMLDivElement>(null);
const [bounds, setBounds] = useState({
left: 0,
......@@ -41,5 +41,3 @@ const DragModal = (props: DragModalProps) => {
</Modal>
);
};
export default DragModal;
import { notification } from "antd";
import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import { selectCurrentPhase } from "@/reducers/duel/phaseSlice";
import { notification } from "antd";
import { selectDuelResult, selectWaiting } from "@/reducers/duel/mod";
import { useNavigate } from "react-router-dom";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { selectCurrentPhase } from "@/reducers/duel/phaseSlice";
import MsgWin = ygopro.StocGameMessage.MsgWin;
import NeosConfig from "../../../neos.config.json";
import { useConfig } from "@/config";
const HintNotification = () => {
const NeosConfig = useConfig();
export const HintNotification = () => {
const hint = useAppSelector(selectHint);
const currentPhase = useAppSelector(selectCurrentPhase);
const waiting = useAppSelector(selectWaiting);
......@@ -67,5 +69,3 @@ const HintNotification = () => {
return <>{contextHolder}</>;
};
export default HintNotification;
export * from "./Alert";
export * from "./CardListModal";
export * from "./CardModal";
export * from "./CheckCardModal";
export * from "./CheckCardModalV2";
export * from "./CheckCardModalV3";
export * from "./CheckCounterModal";
export * from "./DragModal";
export * from "./HintNotification";
export * from "./OptionModal";
export * from "./Phase";
export * from "./PositionModal";
export * from "./SendBox";
export * from "./SortCardModal";
export * from "./Status";
export * from "./TimeLine";
export * from "./YesNoModal";
import { CheckCard } from "@ant-design/pro-components";
import { Button } from "antd";
import React, { useState } from "react";
import { sendSelectOptionResponse } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import { store } from "@/store";
import { Button } from "antd";
import { CheckCard } from "@ant-design/pro-components";
import { resetOptionModal, setOptionModalIsOpen } from "@/reducers/duel/mod";
import {
selectOptionModalIsOpen,
selectOptionModalOptions,
} from "@/reducers/duel/modal/mod";
import { sendSelectOptionResponse } from "@/api/ocgcore/ocgHelper";
import { resetOptionModal, setOptionModalIsOpen } from "@/reducers/duel/mod";
import DragModal from "./dragModal";
import { store } from "@/store";
import { DragModal } from "./DragModal";
const OptionModal = () => {
export const OptionModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectOptionModalIsOpen);
const options = useAppSelector(selectOptionModalOptions);
......@@ -52,5 +54,3 @@ const OptionModal = () => {
</DragModal>
);
};
export default OptionModal;
import Icon from "@ant-design/icons";
import { Button, Modal, Space } from "antd";
import React, { useState } from "react";
import { store } from "@/store";
import { useAppSelector } from "@/hook";
import {
selectCurrentPhase,
selectEnableBp,
selectEnableEp,
selectEnableM2,
} from "@/reducers/duel/phaseSlice";
import {
sendSelectBattleCmdResponse,
sendSelectIdleCmdResponse,
sendSurrender,
} from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import {
clearAllIdleInteractivities,
setEnableBp,
setEnableEp,
setEnableM2,
} from "@/reducers/duel/mod";
import { Button, Modal, Space } from "antd";
import Icon from "@ant-design/icons";
import { ReactComponent as BattleSvg } from "../../../neos-assets/crossed-swords.svg";
import { ReactComponent as Main2Svg } from "../../../neos-assets/sword-in-stone.svg";
import { ReactComponent as EpSvg } from "../../../neos-assets/power-button.svg";
import { ReactComponent as SurrenderSvg } from "../../../neos-assets/truce.svg";
import {
selectCurrentPhase,
selectEnableBp,
selectEnableEp,
selectEnableM2,
} from "@/reducers/duel/phaseSlice";
import { store } from "@/store";
import { ReactComponent as BattleSvg } from "neos-assets/crossed-swords.svg";
import { ReactComponent as EpSvg } from "neos-assets/power-button.svg";
import { ReactComponent as Main2Svg } from "neos-assets/sword-in-stone.svg";
import { ReactComponent as SurrenderSvg } from "neos-assets/truce.svg";
const IconSize = "150%";
const SpaceSize = 16;
......@@ -46,7 +48,7 @@ const PhaseButton = (props: {
);
};
const Phase = () => {
export const Phase = () => {
const dispatch = store.dispatch;
const enableBp = useAppSelector(selectEnableBp);
const enableM2 = useAppSelector(selectEnableM2);
......@@ -141,5 +143,3 @@ const Phase = () => {
</Space>
);
};
export default Phase;
import React, { useState } from "react";
import { useAppSelector } from "@/hook";
import { store } from "@/store";
import { CheckCard } from "@ant-design/pro-components";
import { Button } from "antd";
import { sendSelectPositionResponse } from "@/api/ocgcore/ocgHelper";
import {
selectPositionModalIsOpen,
selectPositionModalPositions,
} from "@/reducers/duel/modal/mod";
import React, { useState } from "react";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { sendSelectPositionResponse } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import {
resetPositionModal,
setPositionModalIsOpen,
} from "@/reducers/duel/mod";
import { CheckCard } from "@ant-design/pro-components";
import DragModal from "./dragModal";
import {
selectPositionModalIsOpen,
selectPositionModalPositions,
} from "@/reducers/duel/modal/mod";
import { store } from "@/store";
const PositionModal = () => {
import { DragModal } from "./DragModal";
export const PositionModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectPositionModalIsOpen);
const positions = useAppSelector(selectPositionModalPositions);
......@@ -82,5 +84,3 @@ function cardPositionToChinese(position: ygopro.CardPosition): string {
}
}
}
export default PositionModal;
import React, { useState } from "react";
import { Input, Button, Row, Col } from "antd";
import { SendOutlined } from "@ant-design/icons";
import { Button, Col, Input, Row } from "antd";
import React, { useState } from "react";
import { sendChat } from "@/api/ocgcore/ocgHelper";
const SendBox = () => {
export const SendBox = () => {
const [content, setContent] = useState("");
return (
<>
......@@ -32,5 +33,3 @@ const SendBox = () => {
</>
);
};
export default SendBox;
import React, { useEffect, useState } from "react";
import {
DndContext,
closestCenter,
DndContext,
DragEndEvent,
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
DragEndEvent,
} from "@dnd-kit/core";
import {
arrayMove,
SortableContext,
sortableKeyboardCoordinates,
verticalListSortingStrategy,
useSortable,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Button, Card, Modal } from "antd";
import React, { useEffect, useState } from "react";
import { CardMeta } from "@/api/cards";
import { sendSortCardResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { resetSortCardModal } from "@/reducers/duel/mod";
import { selectSortCardModal } from "@/reducers/duel/modal/sortCardModalSlice";
import { sendSortCardResponse } from "@/api/ocgcore/ocgHelper";
import { store } from "@/store";
import { resetSortCardModal } from "@/reducers/duel/mod";
import { Modal, Button, Card } from "antd";
import { CardMeta } from "@/api/cards";
import NeosConfig from "../../../neos.config.json";
const SortCardModal = () => {
const NeosConfig = useConfig();
export const SortCardModal = () => {
const dispatch = store.dispatch;
const state = useAppSelector(selectSortCardModal);
const isOpen = state.isOpen;
......@@ -111,5 +114,3 @@ const SortableItem = (props: { id: number; meta: CardMeta }) => {
</div>
);
};
export default SortCardModal;
import React from "react";
import { UserOutlined } from "@ant-design/icons";
import { Avatar } from "antd";
import { CheckCard } from "@ant-design/pro-components";
import NeosConfig from "../../../neos.config.json";
import { Avatar } from "antd";
import React from "react";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
selectMeInitInfo,
......@@ -10,12 +11,14 @@ import {
} from "@/reducers/duel/initInfoSlice";
import { selectWaiting } from "@/reducers/duel/mod";
const NeosConfig = useConfig();
const Config = NeosConfig.ui.status;
const avatarSize = 40;
const ME_VALUE = "myself";
const OP_VALUE = "opponent";
const PlayerStatus = () => {
export const PlayerStatus = () => {
const meInfo = useAppSelector(selectMeInitInfo);
const opInfo = useAppSelector(selectOpInitInfo);
const waiting = useAppSelector(selectWaiting) || false;
......@@ -61,5 +64,3 @@ const PlayerStatus = () => {
</CheckCard.Group>
);
};
export default PlayerStatus;
import React, { useEffect, useState } from "react";
import { Timeline, TimelineItemProps } from "antd";
import { MessageOutlined } from "@ant-design/icons";
import { Timeline, TimelineItemProps } from "antd";
import React, { useEffect, useState } from "react";
import { useAppSelector } from "@/hook";
import { selectChat } from "@/reducers/chatSlice";
const DuelTimeLine = () => {
export const DuelTimeLine = () => {
const [items, setItems] = useState<TimelineItemProps[]>([]);
const chat = useAppSelector(selectChat);
......@@ -22,5 +23,3 @@ const DuelTimeLine = () => {
return <Timeline items={items} />;
};
export default DuelTimeLine;
import React from "react";
import { useAppSelector } from "@/hook";
import { store } from "@/store";
import { Button } from "antd";
import React from "react";
import { sendSelectEffectYnResponse } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import { selectHint } from "@/reducers/duel/hintSlice";
import { setYesNoModalIsOpen } from "@/reducers/duel/mod";
import {
selectYesNoModalIsOpen,
selectYesNOModalMsg,
} from "@/reducers/duel/modal/mod";
import { setYesNoModalIsOpen } from "@/reducers/duel/mod";
import DragModal from "./dragModal";
import { selectHint } from "@/reducers/duel/hintSlice";
import { store } from "@/store";
import { DragModal } from "./DragModal";
const YesNoModal = () => {
export const YesNoModal = () => {
const dispatch = store.dispatch;
const isOpen = useAppSelector(selectYesNoModalIsOpen);
const msg = useAppSelector(selectYesNOModalMsg);
......@@ -46,5 +48,3 @@ const YesNoModal = () => {
/>
);
};
export default YesNoModal;
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
selectMeExclusion,
selectopExclusion,
} from "@/reducers/duel/exclusionSlice";
import SingleSlot, { Depth } from "./singleSlot";
import NeosConfig from "../../../neos.config.json";
import { cardSlotRotation } from "./util";
selectMeBanishedZone,
selectOpBanishedZone,
} from "@/reducers/duel/banishedZoneSlice";
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
const Exclusion = () => {
const meExclusion = useAppSelector(selectMeExclusion).inner;
const opExclusion = useAppSelector(selectopExclusion).inner;
const NeosConfig = useConfig();
export const BanishedZone = () => {
const meBanishedZone = useAppSelector(selectMeBanishedZone).inner;
const opBanishedZone = useAppSelector(selectOpBanishedZone).inner;
return (
<>
<SingleSlot
state={meExclusion}
position={exclusionPosition(0, meExclusion.length)}
state={meBanishedZone}
position={banishedZonePosition(0, meBanishedZone.length)}
rotation={cardSlotRotation(false)}
/>
<SingleSlot
state={opExclusion}
position={exclusionPosition(1, opExclusion.length)}
state={opBanishedZone}
position={banishedZonePosition(1, opBanishedZone.length)}
rotation={cardSlotRotation(true)}
/>
</>
);
};
const exclusionPosition = (player: number, exclusionLength: number) => {
const banishedZonePosition = (player: number, banishedZoneLength: number) => {
const x = player == 0 ? 3.2 : -3.2;
const y = (Depth * exclusionLength) / 2 + NeosConfig.ui.card.floating;
const y = (Depth * banishedZoneLength) / 2 + NeosConfig.ui.card.floating;
const z = player == 0 ? -0.7 : 0.7;
return new BABYLON.Vector3(x, y, z);
};
export default Exclusion;
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectMeDeck, selectOpDeck } from "@/reducers/duel/deckSlice";
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
const NeosConfig = useConfig();
export const CommonDeck = () => {
const meDeck = useAppSelector(selectMeDeck).inner;
const opDeck = useAppSelector(selectOpDeck).inner;
return (
<>
<SingleSlot
state={meDeck}
position={deckPosition(0, meDeck.length)}
rotation={cardSlotRotation(false)}
/>
<SingleSlot
state={opDeck}
position={deckPosition(1, opDeck.length)}
rotation={cardSlotRotation(true)}
/>
</>
);
};
const deckPosition = (player: number, deckLength: number) => {
const x = player == 0 ? 3.2 : -3.2;
const y = (Depth * deckLength) / 2 + NeosConfig.ui.card.floating;
const z = player == 0 ? -3.3 : 3.3;
return new BABYLON.Vector3(x, y, z);
};
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
selectMeExtraDeck,
selectOpExtraDeck,
} from "@/reducers/duel/extraDeckSlice";
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
const NeosConfig = useConfig();
export const ExtraDeck = () => {
const meExtraDeck = useAppSelector(selectMeExtraDeck).inner;
const opExtraDeck = useAppSelector(selectOpExtraDeck).inner;
return (
<>
<SingleSlot
state={meExtraDeck}
position={extraDeckPosition(0, meExtraDeck.length)}
rotation={cardSlotRotation(false)}
/>
<SingleSlot
state={opExtraDeck}
position={extraDeckPosition(1, opExtraDeck.length)}
rotation={cardSlotRotation(true)}
/>
</>
);
};
const extraDeckPosition = (player: number, deckLength: number) => {
const x = player == 0 ? -3.3 : 3.3;
const y = (Depth & deckLength) / 2 + NeosConfig.ui.card.floating;
const z = player == 0 ? -3.3 : 3.3;
return new BABYLON.Vector3(x, y, z);
};
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectMeMagics, selectOpMagics } from "@/reducers/duel/magicSlice";
import { clearMagicPlaceInteractivities } from "@/reducers/duel/mod";
import { cardSlotRotation } from "../utils";
import { FixedSlot } from "./FixedSlot";
import { Depth } from "./SingleSlot";
const NeosConfig = useConfig();
export const Field = () => {
const meField = useAppSelector(selectMeMagics).inner.find(
(_, sequence) => sequence == 5
);
const opField = useAppSelector(selectOpMagics).inner.find(
(_, sequence) => sequence == 5
);
return (
<>
{meField ? (
<FixedSlot
state={meField}
sequence={0}
position={fieldPosition(0)}
rotation={cardSlotRotation(false)}
clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
/>
) : (
<></>
)}
{opField ? (
<FixedSlot
state={opField}
sequence={0}
position={fieldPosition(1)}
rotation={cardSlotRotation(true)}
clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
/>
) : (
<></>
)}
</>
);
};
const fieldPosition = (player: number) => {
const x = player == 0 ? -3.3 : 3.3;
const y = Depth / 2 + NeosConfig.ui.card.floating;
const z = player == 0 ? -2.0 : 2.0;
return new BABYLON.Vector3(x, y, z);
};
import * as BABYLON from "@babylonjs/core";
import { ActionCreatorWithPayload } from "@reduxjs/toolkit";
import { useRef } from "react";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { sendSelectPlaceResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useClick } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
import {
setCardListModalInfo,
setCardListModalIsOpen,
setCardModalCounters,
setCardModalInteractivies,
setCardModalIsOpen,
setCardModalMeta,
} from "@/reducers/duel/mod";
import { store } from "@/store";
import { interactTypeToString } from "../utils";
const NeosConfig = useConfig();
const transform = NeosConfig.ui.card.transform;
const defenceRotation = NeosConfig.ui.card.defenceRotation;
const cardDefenceRotation = new BABYLON.Vector3(
defenceRotation.x,
defenceRotation.y,
defenceRotation.z
);
export const FixedSlot = (props: {
state: CardState;
sequence: number;
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
deffenseRotation?: BABYLON.Vector3;
clearPlaceInteractivitiesAction: ActionCreatorWithPayload<number, string>;
}) => {
const planeRef = useRef(null);
const rotation =
props.state.location.position === ygopro.CardPosition.DEFENSE ||
props.state.location.position === ygopro.CardPosition.FACEUP_DEFENSE ||
props.state.location.position === ygopro.CardPosition.FACEDOWN_DEFENSE
? props.deffenseRotation || cardDefenceRotation
: props.rotation;
const edgesWidth = 2.0;
const edgesColor = BABYLON.Color4.FromColor3(BABYLON.Color3.Yellow());
const dispatch = store.dispatch;
const faceDown =
props.state.location.position === ygopro.CardPosition.FACEDOWN_DEFENSE ||
props.state.location.position === ygopro.CardPosition.FACEDOWN_ATTACK ||
props.state.location.position === ygopro.CardPosition.FACEDOWN;
useClick(
(_event) => {
if (props.state.placeInteractivities) {
sendSelectPlaceResponse(props.state.placeInteractivities.response);
dispatch(props.clearPlaceInteractivitiesAction(0));
dispatch(props.clearPlaceInteractivitiesAction(1));
} else if (props.state.occupant) {
// 中央弹窗展示选中卡牌信息
dispatch(setCardModalMeta(props.state.occupant));
dispatch(
setCardModalInteractivies(
props.state.idleInteractivities.map((interactivity) => {
return {
desc: interactTypeToString(interactivity.interactType),
response: interactivity.response,
};
})
)
);
dispatch(setCardModalCounters(props.state.counters));
dispatch(setCardModalIsOpen(true));
// 侧边栏展示超量素材信息
if (
props.state.overlay_materials &&
props.state.overlay_materials.length > 0
) {
dispatch(
setCardListModalInfo(
props.state.overlay_materials?.map((overlay) => {
return {
meta: overlay,
interactivies: [],
};
}) || []
)
);
dispatch(setCardListModalIsOpen(true));
}
}
},
planeRef,
[props.state]
);
return (
<plane
name={`fixedslot-${props.sequence}`}
ref={planeRef}
width={transform.x}
height={transform.y}
position={props.position}
rotation={rotation}
enableEdgesRendering
edgesWidth={
props.state.placeInteractivities ||
props.state.idleInteractivities.length > 0
? edgesWidth
: 0
}
edgesColor={edgesColor}
>
<standardMaterial
name={`fixedslot-mat-${props.sequence}`}
diffuseTexture={
props.state.occupant
? faceDown
? new BABYLON.Texture(`${NeosConfig.assetsPath}/card_back.jpg`)
: new BABYLON.Texture(
`${NeosConfig.cardImgUrl}/${props.state.occupant.id}.jpg`
)
: new BABYLON.Texture(`${NeosConfig.assetsPath}/card_slot.png`)
}
alpha={props.state.occupant ? 1 : 0}
></standardMaterial>
</plane>
);
};
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
selectMeCemetery,
selectOpCemetery,
selectMeGraveyard,
selectOpGraveyard,
} from "@/reducers/duel/cemeretySlice";
import { useAppSelector } from "@/hook";
import SingleSlot, { Depth } from "./singleSlot";
import { cardSlotRotation } from "./util";
import NeosConfig from "../../../neos.config.json";
const Cemeteries = () => {
const meCemetery = useAppSelector(selectMeCemetery).inner;
const opCemetery = useAppSelector(selectOpCemetery).inner;
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
const NeosConfig = useConfig();
export const Graveyard = () => {
const meGraveyard = useAppSelector(selectMeGraveyard).inner;
const opGraveyard = useAppSelector(selectOpGraveyard).inner;
return (
<>
<SingleSlot
state={meCemetery}
position={cemeteryPosition(0, meCemetery.length)}
state={meGraveyard}
position={graveyardPosition(0, meGraveyard.length)}
rotation={cardSlotRotation(false)}
/>
<SingleSlot
state={opCemetery}
position={cemeteryPosition(1, opCemetery.length)}
state={opGraveyard}
position={graveyardPosition(1, opGraveyard.length)}
rotation={cardSlotRotation(true)}
/>
</>
);
};
const cemeteryPosition = (player: number, cemeteryLength: number) => {
const graveyardPosition = (player: number, graveyardLength: number) => {
const x = player == 0 ? 3.2 : -3.2;
const y = (Depth * cemeteryLength) / 2 + NeosConfig.ui.card.floating;
const y = (Depth * graveyardLength) / 2 + NeosConfig.ui.card.floating;
const z = player == 0 ? -2.0 : 2.0;
return new BABYLON.Vector3(x, y, z);
};
export default Cemeteries;
import * as BABYLON from "@babylonjs/core";
import { useEffect, useRef, useState } from "react";
import { useHover } from "react-babylonjs";
import { useConfig } from "@/config";
import { useAppSelector, useClick } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
import { selectMeHands, selectOpHands } from "@/reducers/duel/handsSlice";
import {
setCardModalInteractivies,
setCardModalIsOpen,
setCardModalMeta,
} from "@/reducers/duel/mod";
import { store } from "@/store";
import { animated, useSpring } from "../spring";
import { interactTypeToString, zip } from "../utils";
const NeosConfig = useConfig();
const groundShape = NeosConfig.ui.ground;
const left = -(groundShape.width / 2);
const handShape = NeosConfig.ui.card.transform;
const rotation = NeosConfig.ui.card.handRotation;
const handRotation = new BABYLON.Vector3(rotation.x, rotation.y, rotation.z);
const hoverScaling = NeosConfig.ui.card.handHoverScaling;
export const Hands = () => {
const meHands = useAppSelector(selectMeHands).inner;
const meHandPositions = handPositons(0, meHands);
const opHands = useAppSelector(selectOpHands).inner;
const opHandPositions = handPositons(1, opHands);
return (
<>
{zip(meHands, meHandPositions).map(([hand, position], idx) => {
return (
<CHand
key={idx}
state={hand}
sequence={idx}
position={position}
rotation={handRotation}
cover={(id) => `${NeosConfig.cardImgUrl}/${id}.jpg`}
/>
);
})}
{zip(opHands, opHandPositions).map(([hand, position], idx) => {
return (
<CHand
key={idx}
state={hand}
sequence={idx}
position={position}
rotation={handRotation}
cover={(_) => `${NeosConfig.assetsPath}/card_back.jpg`}
/>
);
})}
</>
);
};
const CHand = (props: {
state: CardState;
sequence: number;
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
cover: (id: number) => string;
}) => {
const hoverScale = new BABYLON.Vector3(
hoverScaling.x,
hoverScaling.y,
hoverScaling.z
);
const defaultScale = new BABYLON.Vector3(1, 1, 1);
const edgesWidth = 2.0;
const edgesColor = BABYLON.Color4.FromColor3(BABYLON.Color3.Yellow());
const planeRef = useRef(null);
const state = props.state;
const [hovered, setHovered] = useState(false);
const position = props.position;
const dispatch = store.dispatch;
const [spring, api] = useSpring(
() => ({
from: {
position,
},
config: {
mass: 1.0,
tension: 170,
friction: 900,
precision: 0.01,
velocity: 0.0,
clamp: true,
duration: 2000,
},
}),
[]
);
useEffect(() => {
api.start({
position,
});
}, [position]);
useHover(
() => setHovered(true),
() => setHovered(false),
planeRef
);
useClick(
() => {
if (state.occupant) {
dispatch(setCardModalMeta(state.occupant));
}
dispatch(
setCardModalInteractivies(
state.idleInteractivities.map((interactive) => {
return {
desc: interactTypeToString(interactive.interactType),
response: interactive.response,
};
})
)
);
dispatch(setCardModalIsOpen(true));
},
planeRef,
[state]
);
return (
// @ts-ignore
<animated.transformNode name="">
<animated.plane
name={`hand-${props.sequence}`}
ref={planeRef}
width={handShape.x}
height={handShape.y}
scaling={hovered ? hoverScale : defaultScale}
position={spring.position}
rotation={props.rotation}
enableEdgesRendering
edgesWidth={
state.idleInteractivities.length > 0 || state.placeInteractivities
? edgesWidth
: 0
}
edgesColor={edgesColor}
>
<animated.standardMaterial
name={`hand-mat-${props.sequence}`}
diffuseTexture={
new BABYLON.Texture(props.cover(state.occupant?.id || 0))
}
/>
</animated.plane>
</animated.transformNode>
);
};
const handPositons = (player: number, hands: CardState[]) => {
const gap = groundShape.width / (hands.length - 1);
const x = (idx: number) =>
player == 0 ? left + gap * idx : -left - gap * idx;
const y = handShape.y / 2;
const z =
player == 0 ? -(groundShape.height / 2) - 1 : groundShape.height / 2 + 1;
return hands.map((_, idx) => new BABYLON.Vector3(x(idx), y, z));
};
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
import { selectMeMagics, selectOpMagics } from "@/reducers/duel/magicSlice";
import { clearMagicPlaceInteractivities } from "@/reducers/duel/mod";
import { cardSlotRotation, zip } from "../utils";
import { FixedSlot } from "./FixedSlot";
const NeosConfig = useConfig();
// TODO: use config
const left = -2.15;
const gap = 1.05;
const transform = NeosConfig.ui.card.transform;
export const Magics = () => {
const meMagics = useAppSelector(selectMeMagics).inner;
const meMagicPositions = magicPositions(0, meMagics);
const opMagics = useAppSelector(selectOpMagics).inner;
const opMagicPositions = magicPositions(1, opMagics);
return (
<>
{zip(meMagics, meMagicPositions)
.slice(0, 5)
.map(([magic, position], sequence) => {
return (
<FixedSlot
state={magic}
key={sequence}
sequence={sequence}
position={position}
rotation={cardSlotRotation(false)}
clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
/>
);
})}
{zip(opMagics, opMagicPositions)
.slice(0, 5)
.map(([magic, position], sequence) => {
return (
<FixedSlot
state={magic}
key={sequence}
sequence={sequence}
position={position}
rotation={cardSlotRotation(true)}
clearPlaceInteractivitiesAction={clearMagicPlaceInteractivities}
/>
);
})}
</>
);
};
const magicPositions = (player: number, magics: CardState[]) => {
const x = (sequence: number) =>
player == 0 ? left + gap * sequence : -left - gap * sequence;
const y = transform.z / 2 + NeosConfig.ui.card.floating;
const z = player == 0 ? -2.6 : 2.6;
return magics.map((_, sequence) => new BABYLON.Vector3(x(sequence), y, z));
};
import "react-babylonjs";
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
import { clearMonsterPlaceInteractivities } from "@/reducers/duel/mod";
import {
selectMeMonsters,
selectOpMonsters,
} from "@/reducers/duel/monstersSlice";
import { cardSlotDefenceRotation, cardSlotRotation, zip } from "../utils";
import { FixedSlot } from "./FixedSlot";
const NeosConfig = useConfig();
const transform = NeosConfig.ui.card.transform;
const floating = NeosConfig.ui.card.floating;
const left = -2.15; // TODO: config
const gap = 1.05;
export const Monsters = () => {
const meMonsters = useAppSelector(selectMeMonsters).inner;
const meMonsterPositions = monsterPositions(0, meMonsters);
const opMonsters = useAppSelector(selectOpMonsters).inner;
const opMonsterPositions = monsterPositions(1, opMonsters);
return (
<>
{zip(meMonsters, meMonsterPositions)
.slice(0, 5)
.map(([monster, position], sequence) => (
<FixedSlot
state={monster}
key={sequence}
sequence={sequence}
position={position}
rotation={cardSlotRotation(false)}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
/>
))}
{zip(opMonsters, opMonsterPositions)
.slice(0, 5)
.map(([monster, position], sequence) => (
<FixedSlot
state={monster}
key={sequence}
sequence={sequence}
position={position}
rotation={cardSlotRotation(true)}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
/>
))}
<ExtraMonsters meMonsters={meMonsters} opMonsters={opMonsters} />
</>
);
};
// TODO: use props and redux
const ExtraMonsters = (props: {
meMonsters: CardState[];
opMonsters: CardState[];
}) => {
const meLeft = props.meMonsters.find((_, sequence) => sequence == 5);
const meRight = props.meMonsters.find((_, sequence) => sequence == 6);
const opLeft = props.opMonsters.find((_, sequence) => sequence == 5);
const opRight = props.opMonsters.find((_, sequence) => sequence == 6);
const leftPosition = new BABYLON.Vector3(-1.1, transform.z / 2 + floating, 0);
const rightPosition = new BABYLON.Vector3(1.1, transform.z / 2 + floating, 0);
const meRotation = cardSlotRotation(false);
const opRotation = cardSlotRotation(true);
return (
<>
{meLeft ? (
<FixedSlot
state={meLeft}
sequence={5}
position={leftPosition}
rotation={meRotation}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
/>
) : (
<></>
)}
{meRight ? (
<FixedSlot
state={meRight}
sequence={6}
position={rightPosition}
rotation={meRotation}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
/>
) : (
<></>
)}
{opLeft ? (
<FixedSlot
state={opLeft}
sequence={5}
position={rightPosition}
rotation={opRotation}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
/>
) : (
<></>
)}
{opRight ? (
<FixedSlot
state={opRight}
sequence={6}
position={leftPosition}
rotation={opRotation}
deffenseRotation={cardSlotDefenceRotation()}
clearPlaceInteractivitiesAction={clearMonsterPlaceInteractivities}
/>
) : (
<></>
)}
</>
);
};
const monsterPositions = (player: number, monsters: CardState[]) => {
const x = (sequence: number) =>
player == 0 ? left + gap * sequence : -left - gap * sequence;
const y = transform.z / 2 + floating;
const z = player == 0 ? -1.35 : 1.35;
return monsters.map((_, sequence) => new BABYLON.Vector3(x(sequence), y, z));
};
import * as BABYLON from "@babylonjs/core";
import { useRef } from "react";
import { useConfig } from "@/config";
import { useClick } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
import {
setCardListModalInfo,
setCardListModalIsOpen,
} from "@/reducers/duel/mod";
import { store } from "@/store";
import { interactTypeToString } from "../utils";
const NeosConfig = useConfig();
const transform = NeosConfig.ui.card.transform;
export const Depth = 0.005;
export const SingleSlot = (props: {
state: CardState[];
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
}) => {
const boxRef = useRef(null);
const dispatch = store.dispatch;
const edgeRender =
props.state.find((item) =>
item === undefined ? false : item.idleInteractivities.length > 0
) !== undefined;
const edgesWidth = 2.0;
const edgesColor = BABYLON.Color4.FromColor3(BABYLON.Color3.Yellow());
useClick(
(_event) => {
if (props.state.length != 0) {
dispatch(
setCardListModalInfo(
props.state
.filter(
(item) => item.occupant !== undefined && item.occupant.id !== 0
)
.map((item) => {
return {
meta: item.occupant,
interactivies: item.idleInteractivities.map((interactivy) => {
return {
desc: interactTypeToString(interactivy.interactType),
response: interactivy.response,
};
}),
};
})
)
);
dispatch(setCardListModalIsOpen(true));
}
},
boxRef,
[props.state]
);
return (
<box
name="single-slot"
ref={boxRef}
scaling={
new BABYLON.Vector3(
transform.x,
transform.y,
Depth * props.state.length
)
}
position={props.position}
rotation={props.rotation}
enableEdgesRendering
edgesWidth={edgeRender ? edgesWidth : 0}
edgesColor={edgesColor}
>
<standardMaterial
name="single-slot-mat"
diffuseTexture={
new BABYLON.Texture(`${NeosConfig.assetsPath}/card_back.jpg`)
}
alpha={props.state.length == 0 ? 0 : 1}
/>
</box>
);
};
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectMeDeck, selectOpDeck } from "@/reducers/duel/deckSlice";
import SingleSlot, { Depth } from "./singleSlot";
import { cardSlotRotation } from "./util";
import NeosConfig from "../../../neos.config.json";
const CommonDeck = () => {
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
const NeosConfig = useConfig();
export const CommonDeck = () => {
const meDeck = useAppSelector(selectMeDeck).inner;
const opDeck = useAppSelector(selectOpDeck).inner;
......@@ -32,5 +35,3 @@ const deckPosition = (player: number, deckLength: number) => {
return new BABYLON.Vector3(x, y, z);
};
export default CommonDeck;
import SingleSlot, { Depth } from "./singleSlot";
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import {
selectMeExtraDeck,
selectOpExtraDeck,
} from "@/reducers/duel/extraDeckSlice";
import NeosConfig from "../../../neos.config.json";
import { cardSlotRotation } from "./util";
const ExtraDeck = () => {
import { cardSlotRotation } from "../utils";
import { Depth, SingleSlot } from "./SingleSlot";
const NeosConfig = useConfig();
export const ExtraDeck = () => {
const meExtraDeck = useAppSelector(selectMeExtraDeck).inner;
const opExtraDeck = useAppSelector(selectOpExtraDeck).inner;
......@@ -35,5 +39,3 @@ const extraDeckPosition = (player: number, deckLength: number) => {
return new BABYLON.Vector3(x, y, z);
};
export default ExtraDeck;
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectMeMagics, selectOpMagics } from "@/reducers/duel/magicSlice";
import { clearMagicPlaceInteractivities } from "@/reducers/duel/mod";
import FixedSlot from "./fixedSlot";
import { Depth } from "./singleSlot";
import NeosConfig from "../../../neos.config.json";
import { cardSlotRotation } from "./util";
const Field = () => {
import { cardSlotRotation } from "../utils";
import { FixedSlot } from "./FixedSlot";
import { Depth } from "./SingleSlot";
const NeosConfig = useConfig();
export const Field = () => {
const meField = useAppSelector(selectMeMagics).inner.find(
(_, sequence) => sequence == 5
);
......@@ -50,5 +53,3 @@ const fieldPosition = (player: number) => {
return new BABYLON.Vector3(x, y, z);
};
export default Field;
import * as BABYLON from "@babylonjs/core";
import { store } from "@/store";
import { CardState } from "@/reducers/duel/generic";
import { ActionCreatorWithPayload } from "@reduxjs/toolkit";
import { useRef } from "react";
import { useClick } from "./hook";
import { sendSelectPlaceResponse } from "@/api/ocgcore/ocgHelper";
import { ygopro } from "@/api/ocgcore/idl/ocgcore";
import { sendSelectPlaceResponse } from "@/api/ocgcore/ocgHelper";
import { useConfig } from "@/config";
import { useClick } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
import {
setCardListModalInfo,
setCardListModalIsOpen,
......@@ -13,9 +15,11 @@ import {
setCardModalIsOpen,
setCardModalMeta,
} from "@/reducers/duel/mod";
import { ActionCreatorWithPayload } from "@reduxjs/toolkit";
import { interactTypeToString } from "./util";
import NeosConfig from "../../../neos.config.json";
import { store } from "@/store";
import { interactTypeToString } from "../utils";
const NeosConfig = useConfig();
const transform = NeosConfig.ui.card.transform;
const defenceRotation = NeosConfig.ui.card.defenceRotation;
......@@ -25,7 +29,7 @@ const cardDefenceRotation = new BABYLON.Vector3(
defenceRotation.z
);
const FixedSlot = (props: {
export const FixedSlot = (props: {
state: CardState;
sequence: number;
position: BABYLON.Vector3;
......@@ -128,5 +132,3 @@ const FixedSlot = (props: {
</plane>
);
};
export default FixedSlot;
import * as BABYLON from "@babylonjs/core";
import { useAppSelector } from "@/hook";
import { selectMeHands, selectOpHands } from "@/reducers/duel/handsSlice";
import { useEffect, useRef, useState } from "react";
import { useHover } from "react-babylonjs";
import { useConfig } from "@/config";
import { useAppSelector, useClick } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
import { selectMeHands, selectOpHands } from "@/reducers/duel/handsSlice";
import {
setCardModalInteractivies,
setCardModalIsOpen,
setCardModalMeta,
setCardModalInteractivies,
} from "@/reducers/duel/mod";
import { store } from "@/store";
import { useHover } from "react-babylonjs";
import { useClick } from "./hook";
import { useState, useRef, useEffect } from "react";
import { useSpring, animated } from "./spring";
import { zip, interactTypeToString } from "./util";
import NeosConfig from "../../../neos.config.json";
import { animated, useSpring } from "../spring";
import { interactTypeToString, zip } from "../utils";
const NeosConfig = useConfig();
const groundShape = NeosConfig.ui.ground;
const left = -(groundShape.width / 2);
......@@ -22,7 +25,7 @@ const rotation = NeosConfig.ui.card.handRotation;
const handRotation = new BABYLON.Vector3(rotation.x, rotation.y, rotation.z);
const hoverScaling = NeosConfig.ui.card.handHoverScaling;
const Hands = () => {
export const Hands = () => {
const meHands = useAppSelector(selectMeHands).inner;
const meHandPositions = handPositons(0, meHands);
const opHands = useAppSelector(selectOpHands).inner;
......@@ -169,5 +172,3 @@ const handPositons = (player: number, hands: CardState[]) => {
return hands.map((_, idx) => new BABYLON.Vector3(x(idx), y, z));
};
export default Hands;
export * from "./BanishedZone";
export * from "./Deck";
export * from "./ExtraDeck";
export * from "./Field";
export * from "./FixedSlot";
export * from "./Graveyard";
export * from "./Hands";
export * from "./Magics";
export * from "./Monsters";
export * from "./SingleSlot";
import * as BABYLON from "@babylonjs/core";
import { selectMeMagics, selectOpMagics } from "@/reducers/duel/magicSlice";
import { CardState } from "@/reducers/duel/generic";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { zip, cardSlotRotation } from "./util";
import FixedSlot from "./fixedSlot";
import { CardState } from "@/reducers/duel/generic";
import { selectMeMagics, selectOpMagics } from "@/reducers/duel/magicSlice";
import { clearMagicPlaceInteractivities } from "@/reducers/duel/mod";
import NeosConfig from "../../../neos.config.json";
import { cardSlotRotation, zip } from "../utils";
import { FixedSlot } from "./FixedSlot";
const NeosConfig = useConfig();
// TODO: use config
const left = -2.15;
const gap = 1.05;
const transform = NeosConfig.ui.card.transform;
const Magics = () => {
export const Magics = () => {
const meMagics = useAppSelector(selectMeMagics).inner;
const meMagicPositions = magicPositions(0, meMagics);
const opMagics = useAppSelector(selectOpMagics).inner;
......@@ -60,5 +63,3 @@ const magicPositions = (player: number, magics: CardState[]) => {
return magics.map((_, sequence) => new BABYLON.Vector3(x(sequence), y, z));
};
export default Magics;
import * as BABYLON from "@babylonjs/core";
import { CardState } from "@/reducers/duel/generic";
import "react-babylonjs";
import * as BABYLON from "@babylonjs/core";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
import { clearMonsterPlaceInteractivities } from "@/reducers/duel/mod";
import {
selectMeMonsters,
selectOpMonsters,
} from "@/reducers/duel/monstersSlice";
import { zip, cardSlotRotation, cardSlotDefenceRotation } from "./util";
import FixedSlot from "./fixedSlot";
import { clearMonsterPlaceInteractivities } from "@/reducers/duel/mod";
import NeosConfig from "../../../neos.config.json";
import { cardSlotDefenceRotation, cardSlotRotation, zip } from "../utils";
import { FixedSlot } from "./FixedSlot";
const NeosConfig = useConfig();
const transform = NeosConfig.ui.card.transform;
const floating = NeosConfig.ui.card.floating;
const left = -2.15; // TODO: config
const gap = 1.05;
const Monsters = () => {
export const Monsters = () => {
const meMonsters = useAppSelector(selectMeMonsters).inner;
const meMonsterPositions = monsterPositions(0, meMonsters);
const opMonsters = useAppSelector(selectOpMonsters).inner;
......@@ -133,5 +137,3 @@ const monsterPositions = (player: number, monsters: CardState[]) => {
return monsters.map((_, sequence) => new BABYLON.Vector3(x(sequence), y, z));
};
export default Monsters;
import * as BABYLON from "@babylonjs/core";
import { CardState } from "@/reducers/duel/generic";
import { store } from "@/store";
import { useClick } from "./hook";
import { useRef } from "react";
import { useConfig } from "@/config";
import { useClick } from "@/hook";
import { CardState } from "@/reducers/duel/generic";
import {
setCardListModalInfo,
setCardListModalIsOpen,
} from "@/reducers/duel/mod";
import { interactTypeToString } from "./util";
import NeosConfig from "../../../neos.config.json";
import { store } from "@/store";
import { interactTypeToString } from "../utils";
const NeosConfig = useConfig();
const transform = NeosConfig.ui.card.transform;
export const Depth = 0.005;
const SingleSlot = (props: {
export const SingleSlot = (props: {
state: CardState[];
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
......@@ -83,5 +86,3 @@ const SingleSlot = (props: {
</box>
);
};
export default SingleSlot;
import React from "react";
import { Layout } from "antd";
import NeosConfig from "../../../neos.config.json";
import React from "react";
import { useConfig } from "@/config";
const NeosConfig = useConfig();
const layoutConfig = NeosConfig.ui.layout;
const { Header, Footer, Sider, Content } = Layout;
......
import React from "react";
import { Engine, Scene } from "react-babylonjs";
import { ReactReduxContext, Provider } from "react-redux";
import * as BABYLON from "@babylonjs/core";
import Hands from "./hands";
import Monsters from "./monsters";
import CardModal from "./cardModal";
import HintNotification from "./hintNotification";
import Magics from "./magics";
import Field from "./field";
import CommonDeck from "./deck";
import Exclusion from "./exclusion";
import Cemeteries from "./cemetery";
import CardListModal from "./cardListModal";
import CheckCardModal from "./checkCardModal";
import YesNoModal from "./yesNoModal";
import PositionModal from "./positionModal";
import OptionModal from "./optionModal";
import Phase from "./phase";
import CheckCardModalV2 from "./checkCardModalV2";
import ExtraDeck from "./extraDeck";
import NeosLayout from "./layout";
import NeosConfig from "../../../neos.config.json";
import DuelTimeLine from "./timeLine";
import { Row } from "antd";
import SendBox from "./sendBox";
import PlayerStatus from "./status";
import Alert from "./alert";
import CheckCardModalV3 from "./checkCardModalV3";
import CheckCounterModal from "./checkCounterModal";
import SortCardModal from "./sortCardModal";
import React from "react";
import { Engine, Scene } from "react-babylonjs";
import { Provider, ReactReduxContext } from "react-redux";
import { useConfig } from "@/config";
import NeosLayout from "./Layout";
import {
Alert,
CardListModal,
CardModal,
CheckCardModal,
CheckCardModalV2,
CheckCardModalV3,
CheckCounterModal,
DuelTimeLine,
HintNotification,
OptionModal,
Phase,
PlayerStatus,
PositionModal,
SendBox,
SortCardModal,
YesNoModal,
} from "./Message";
import {
ExtraDeck,
Field,
Graveyard,
Hands,
Magics,
Monsters,
} from "./PlayMat";
import { BanishedZone } from "./PlayMat/BanishedZone";
import { CommonDeck } from "./PlayMat/Deck";
const NeosConfig = useConfig();
// Ref: https://github.com/brianzinn/react-babylonjs/issues/126
const NeosDuel = () => {
return (
......@@ -81,8 +88,8 @@ const NeosCanvas = () => (
<Field />
<CommonDeck />
<ExtraDeck />
<Cemeteries />
<Exclusion />
<Graveyard />
<BanishedZone />
<Field />
<Ground />
</Provider>
......
// Copyright (c) 2020 hooke
import { CSSProperties, ForwardRefExoticComponent, FC } from "react";
import { FluidValue } from "@react-spring/shared";
import {
AssignableKeys,
ComponentPropsWithRef,
ElementType,
} from "@react-spring/types";
import { CSSProperties, FC, ForwardRefExoticComponent } from "react";
import { Primitives } from "./primitives";
type AnimatedPrimitives = {
......
// Copyright (c) 2020 hooke
import { Globals } from "@react-spring/core";
import "./customProps";
import { createHost } from "@react-spring/animated";
import { Globals } from "@react-spring/core";
import { createStringInterpolator } from "@react-spring/shared";
import { applyInitialPropsToInstance } from "react-babylonjs";
import { primitives } from "./primitives";
import { WithAnimated } from "./animated";
import "./customProps";
import { primitives } from "./primitives";
// todo: frameLoop can use runRenderLoop
Globals.assign({
......
import { Vector3 } from "@babylonjs/core";
import { useConfig } from "@/config";
const NeosConfig = useConfig();
const cardRotation = NeosConfig.ui.card.rotation;
const cardReverseRotation = NeosConfig.ui.card.reverseRotation;
const cardDefenceRotation = NeosConfig.ui.card.defenceRotation;
export function cardSlotRotation(reverse?: boolean) {
if (reverse) {
return new Vector3(
cardReverseRotation.x,
cardReverseRotation.y,
cardReverseRotation.z
);
} else {
return new Vector3(cardRotation.x, cardRotation.y, cardRotation.z);
}
}
export function cardSlotDefenceRotation() {
return new Vector3(
cardDefenceRotation.x,
cardDefenceRotation.y,
cardDefenceRotation.z
);
}
export * from "./cardSlot";
export * from "./interactTypeToString";
export * from "./zip";
import { InteractType } from "@/reducers/duel/generic";
import NeosConfig from "../../../neos.config.json";
import { Vector3 } from "@babylonjs/core";
export function zip<S1, S2>(
firstCollection: Array<S1>,
lastCollection: Array<S2>
): Array<[S1, S2]> {
const length = Math.min(firstCollection.length, lastCollection.length);
const zipped: Array<[S1, S2]> = [];
for (let index = 0; index < length; index++) {
zipped.push([firstCollection[index], lastCollection[index]]);
}
return zipped;
}
export function interactTypeToString(t: InteractType): string {
switch (t) {
......@@ -44,27 +28,3 @@ export function interactTypeToString(t: InteractType): string {
}
}
}
const cardRotation = NeosConfig.ui.card.rotation;
const cardReverseRotation = NeosConfig.ui.card.reverseRotation;
const cardDefenceRotation = NeosConfig.ui.card.defenceRotation;
export function cardSlotRotation(reverse?: boolean) {
if (reverse) {
return new Vector3(
cardReverseRotation.x,
cardReverseRotation.y,
cardReverseRotation.z
);
} else {
return new Vector3(cardRotation.x, cardRotation.y, cardRotation.z);
}
}
export function cardSlotDefenceRotation() {
return new Vector3(
cardDefenceRotation.x,
cardDefenceRotation.y,
cardDefenceRotation.z
);
}
export function zip<S1, S2>(
firstCollection: Array<S1>,
lastCollection: Array<S2>
): Array<[S1, S2]> {
const length = Math.min(firstCollection.length, lastCollection.length);
const zipped: Array<[S1, S2]> = [];
for (let index = 0; index < length; index++) {
zipped.push([firstCollection[index], lastCollection[index]]);
}
return zipped;
}
......@@ -6,13 +6,16 @@
* passWd: 房间密码。
*
* */
import "../styles/core.scss";
import { Input } from "antd";
import React, { useState, ChangeEvent, useEffect } from "react";
import React, { ChangeEvent, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import "../styles/core.scss";
import NeosConfig from "../../neos.config.json";
import { useConfig } from "@/config";
const NeosConfig = useConfig();
const serverConfig = NeosConfig.servers;
const {
defaults: { defaultPlayer, defaultPassword },
......
import {
ScissorOutlined,
SketchOutlined,
TableOutlined,
} from "@ant-design/icons";
import { Button, Modal } from "antd";
import React, { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { sendHandResult, sendTpResult } from "@/api/ocgcore/ocgHelper";
import { useAppSelector } from "@/hook";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import { selectDuelHsStart } from "@/reducers/duel/mod";
import {
selectHandSelectAble,
unSelectHandAble,
selectTpSelectAble,
unSelectHandAble,
unSelectTpAble,
} from "@/reducers/moraSlice";
import { selectDuelHsStart } from "@/reducers/duel/mod";
import { store } from "@/store";
import { useNavigate, useParams } from "react-router-dom";
import { Button, Modal } from "antd";
import {
ScissorOutlined,
SketchOutlined,
TableOutlined,
} from "@ant-design/icons";
const {
automation: { isAiMode, isAiFirst },
......
import React, { Suspense } from "react";
import { Routes, Route } from "react-router-dom";
import { Route, Routes } from "react-router-dom";
import LazyLoad, { Loading } from "./LazyLoad";
const Login = React.lazy(() => import("./Login"));
const WaitRoom = React.lazy(() => import("./WaitRoom"));
const Mora = React.lazy(() => import("./Mora"));
const NeosDuel = React.lazy(() => import("./Duel/main"));
const NeosDuel = React.lazy(() => import("./Duel/Main"));
export default function () {
return (
......
import {
Modal,
Checkbox,
CheckCircleFilled,
LoginOutlined,
LogoutOutlined,
SendOutlined,
TagOutlined,
UploadOutlined,
UserOutlined,
} from "@ant-design/icons";
import type { UploadProps } from "antd";
import {
Avatar,
Space,
Button,
notification,
Upload,
Checkbox,
message,
Modal,
notification,
Select,
Space,
Upload,
} from "antd";
import React, { useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import React, { useState, useEffect } from "react";
import rustInit from "rust-src";
import YGOProDeck from "ygopro-deck-encode";
import { DeckManager, fetchDeck, type IDeck } from "@/api/deck";
import {
sendHsReady,
sendHsStart,
sendUpdateDeck,
} from "@/api/ocgcore/ocgHelper";
import { initStrings } from "@/api/strings";
import { useConfig } from "@/config";
import { useAppSelector } from "@/hook";
import socketMiddleWare, { socketCmd } from "@/middleware/socket";
import sqliteMiddleWare, { sqliteCmd } from "@/middleware/sqlite";
import { store } from "@/store";
import { selectChat } from "@/reducers/chatSlice";
import { initMeExtraDeckMeta } from "@/reducers/duel/extraDeckSlice";
import { selectJoined } from "@/reducers/joinSlice";
import { selectDuelStart } from "@/reducers/moraSlice";
import {
selectIsHost,
selectPlayer0,
selectPlayer1,
} from "@/reducers/playerSlice";
import { useAppSelector } from "@/hook";
import { useConfig } from "@/config";
import { selectJoined } from "@/reducers/joinSlice";
import { selectChat } from "@/reducers/chatSlice";
import { fetchDeck, type IDeck, DeckManager } from "@/api/deck";
import {
sendUpdateDeck,
sendHsReady,
sendHsStart,
} from "@/api/ocgcore/ocgHelper";
import {
UserOutlined,
CheckCircleFilled,
LoginOutlined,
LogoutOutlined,
SendOutlined,
TagOutlined,
UploadOutlined,
} from "@ant-design/icons";
import { initMeExtraDeckMeta } from "@/reducers/duel/extraDeckSlice";
import type { UploadProps } from "antd";
import { selectDuelStart } from "@/reducers/moraSlice";
import NeosConfig from "../../neos.config.json";
import YGOProDeck from "ygopro-deck-encode";
import rustInit from "rust-src";
import { initStrings } from "@/api/strings";
import { store } from "@/store";
const NeosConfig = useConfig();
const READY_STATE = "ready";
......
......@@ -28,6 +28,9 @@
],
"rust-src": [
"./rust-src/pkg"
],
"neos-assets/*": [
"./neos-assets/*"
]
}
},
......
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