Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
N
Neos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
1
Issues
1
List
Boards
Labels
Service Desk
Milestones
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Neos
Commits
332f394c
Commit
332f394c
authored
Aug 19, 2023
by
Chunchi Che
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
支持萌卡匹配(目前仅开放娱乐匹配功能)
parent
6d78b4fb
Changes
30
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
592 additions
and
62 deletions
+592
-62
neos-protobuf
neos-protobuf
+1
-1
src/api/ocgcore/idl/ocgcore.ts
src/api/ocgcore/idl/ocgcore.ts
+198
-4
src/api/ocgcore/ocgAdapter/adapter.ts
src/api/ocgcore/ocgAdapter/adapter.ts
+12
-0
src/api/ocgcore/ocgAdapter/protoDecl.ts
src/api/ocgcore/ocgAdapter/protoDecl.ts
+2
-0
src/api/ocgcore/ocgAdapter/stoc/stocChangeSide.ts
src/api/ocgcore/ocgAdapter/stoc/stocChangeSide.ts
+16
-0
src/api/ocgcore/ocgAdapter/stoc/stocWaitingSide.ts
src/api/ocgcore/ocgAdapter/stoc/stocWaitingSide.ts
+16
-0
src/service/duel/gameMsg.ts
src/service/duel/gameMsg.ts
+3
-7
src/service/duel/start.ts
src/service/duel/start.ts
+17
-4
src/service/mora/selectTp.ts
src/service/mora/selectTp.ts
+7
-3
src/service/onSocketMessage.ts
src/service/onSocketMessage.ts
+18
-0
src/service/onSocketOpen.ts
src/service/onSocketOpen.ts
+1
-1
src/service/room/duelStart.ts
src/service/room/duelStart.ts
+6
-2
src/service/side/changeSide.ts
src/service/side/changeSide.ts
+5
-0
src/service/side/waitingSide.ts
src/service/side/waitingSide.ts
+5
-0
src/stores/index.ts
src/stores/index.ts
+11
-0
src/stores/sideStore.ts
src/stores/sideStore.ts
+27
-0
src/ui/BuildDeck/index.tsx
src/ui/BuildDeck/index.tsx
+1
-1
src/ui/Duel/Main.tsx
src/ui/Duel/Main.tsx
+13
-1
src/ui/Duel/Message/EndModal/index.tsx
src/ui/Duel/Message/EndModal/index.tsx
+3
-5
src/ui/Duel/PlayMat/Mat/index.tsx
src/ui/Duel/PlayMat/Mat/index.tsx
+4
-1
src/ui/Match/MatchModal.tsx
src/ui/Match/MatchModal.tsx
+5
-4
src/ui/Match/ReplayModal.tsx
src/ui/Match/ReplayModal.tsx
+2
-2
src/ui/Match/index.tsx
src/ui/Match/index.tsx
+71
-21
src/ui/Match/util.ts
src/ui/Match/util.ts
+2
-2
src/ui/Side/ChangeSideModal/index.tsx
src/ui/Side/ChangeSideModal/index.tsx
+75
-0
src/ui/Side/TpModal/index.module.scss
src/ui/Side/TpModal/index.module.scss
+12
-0
src/ui/Side/TpModal/index.tsx
src/ui/Side/TpModal/index.tsx
+40
-0
src/ui/Side/index.tsx
src/ui/Side/index.tsx
+2
-0
src/ui/WaitRoom/Popover.tsx
src/ui/WaitRoom/Popover.tsx
+3
-1
src/ui/WaitRoom/index.tsx
src/ui/WaitRoom/index.tsx
+14
-2
No files found.
neos-protobuf
@
a02a8caa
Subproject commit
12f48aa7901f81f4b356b968bc7a8281a1a2d848
Subproject commit
a02a8caa38767bb06e1a9d5be97773af5a0fc4b2
src/api/ocgcore/idl/ocgcore.ts
View file @
332f394c
This diff is collapsed.
Click to expand it.
src/api/ocgcore/ocgAdapter/adapter.ts
View file @
332f394c
import
{
ygopro
}
from
"
../idl/ocgcore
"
;
import
{
YgoProPacket
}
from
"
./packet
"
;
import
{
STOC_CHANGE_SIDE
,
STOC_CHAT
,
STOC_DECK_COUNT
,
STOC_DUEL_START
,
...
...
@@ -15,7 +16,9 @@ import {
STOC_SELECT_TP
,
STOC_TIME_LIMIT
,
STOC_TYPE_CHANGE
,
STOC_WAITING_SIDE
,
}
from
"
./protoDecl
"
;
import
StocChangeSide
from
"
./stoc/stocChangeSide
"
;
import
StocChat
from
"
./stoc/stocChat
"
;
import
StocDeckCount
from
"
./stoc/stocDeckCount
"
;
import
StocDuelStart
from
"
./stoc/stocDuelStart
"
;
...
...
@@ -30,6 +33,7 @@ import StocSelectHand from "./stoc/stocSelectHand";
import
StocSelectTp
from
"
./stoc/stocSelectTp
"
;
import
StocTimeLimit
from
"
./stoc/stocTimeLimit
"
;
import
StocTypeChange
from
"
./stoc/stocTypeChange
"
;
import
StocWaitingSide
from
"
./stoc/stocWaitingSide
"
;
/*
* 将[`ygoProPacket`]对象转换成[`ygopro.YgoStocMsg`]对象
...
...
@@ -97,6 +101,14 @@ export function adaptStoc(packet: YgoProPacket): ygopro.YgoStocMsg {
pb
=
new
StocErrorMsg
(
packet
).
upcast
();
break
;
}
case
STOC_CHANGE_SIDE
:
{
pb
=
new
StocChangeSide
(
packet
).
upcast
();
break
;
}
case
STOC_WAITING_SIDE
:
{
pb
=
new
StocWaitingSide
(
packet
).
upcast
();
break
;
}
default
:
{
break
;
}
...
...
src/api/ocgcore/ocgAdapter/protoDecl.ts
View file @
332f394c
...
...
@@ -31,6 +31,8 @@ export const STOC_DUEL_START = 21;
export
const
STOC_GAME_MSG
=
1
;
export
const
STOC_TIME_LIMIT
=
24
;
export
const
STOC_ERROR_MSG
=
2
;
export
const
STOC_CHANGE_SIDE
=
7
;
export
const
STOC_WAITING_SIDE
=
8
;
export
const
MSG_START
=
4
;
export
const
MSG_DRAW
=
90
;
...
...
src/api/ocgcore/ocgAdapter/stoc/stocChangeSide.ts
0 → 100644
View file @
332f394c
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
StocAdapter
,
YgoProPacket
}
from
"
../packet
"
;
export
default
class
ChangeSide
implements
StocAdapter
{
packet
:
YgoProPacket
;
constructor
(
packet
:
YgoProPacket
)
{
this
.
packet
=
packet
;
}
upcast
():
ygopro
.
YgoStocMsg
{
return
new
ygopro
.
YgoStocMsg
({
stoc_change_side
:
new
ygopro
.
StocChangeSide
({}),
});
}
}
src/api/ocgcore/ocgAdapter/stoc/stocWaitingSide.ts
0 → 100644
View file @
332f394c
import
{
ygopro
}
from
"
../../idl/ocgcore
"
;
import
{
StocAdapter
,
YgoProPacket
}
from
"
../packet
"
;
export
default
class
WaitingSide
implements
StocAdapter
{
packet
:
YgoProPacket
;
constructor
(
packet
:
YgoProPacket
)
{
this
.
packet
=
packet
;
}
upcast
():
ygopro
.
YgoStocMsg
{
return
new
ygopro
.
YgoStocMsg
({
stoc_waiting_side
:
new
ygopro
.
StocWaitingSide
({}),
});
}
}
src/service/duel/gameMsg.ts
View file @
332f394c
...
...
@@ -88,13 +88,9 @@ const ReplayIgnoreMsg = [
"
announce
"
,
];
let
animation
:
Promise
<
unknown
>
=
new
Promise
<
void
>
((
rs
)
=>
rs
());
export
default
async
function
handleGameMsg
(
pb
:
ygopro
.
YgoStocMsg
)
{
animation
=
animation
.
then
(()
=>
_handleGameMsg
(
pb
));
}
async
function
_handleGameMsg
(
pb
:
ygopro
.
YgoStocMsg
)
{
export
default
async
function
handleGameMsg
(
pb
:
ygopro
.
YgoStocMsg
,
):
Promise
<
void
>
{
const
msg
=
pb
.
stoc_game_msg
;
if
(
ActiveList
.
includes
(
msg
.
gameMsg
))
{
...
...
src/service/duel/start.ts
View file @
332f394c
...
...
@@ -6,7 +6,15 @@ import PlayerType = ygopro.StocGameMessage.MsgStart.PlayerType;
import
{
fetchCard
,
ygopro
}
from
"
@/api
"
;
import
{
useConfig
}
from
"
@/config
"
;
import
{
sleep
}
from
"
@/infra
"
;
import
{
cardStore
,
CardType
,
matStore
,
RoomStage
,
roomStore
}
from
"
@/stores
"
;
import
{
cardStore
,
CardType
,
matStore
,
RoomStage
,
roomStore
,
SideStage
,
sideStore
,
}
from
"
@/stores
"
;
import
{
replayStart
}
from
"
@/ui/Match/ReplayModal
"
;
const
TOKEN_SIZE
=
13
;
// 每人场上最多就只可能有13个token
...
...
@@ -19,9 +27,14 @@ export default async (start: ygopro.StocGameMessage.MsgStart) => {
?
1
:
0
;
// 通知房间页面决斗开始
// 这行在该函数中的位置不能随便放,否则可能会block住
roomStore
.
stage
=
RoomStage
.
DUEL_START
;
if
(
sideStore
.
stage
!==
SideStage
.
NONE
)
{
// 更新Side状态
sideStore
.
stage
=
SideStage
.
DUEL_START
;
}
else
{
// 通知房间页面决斗开始
// 这行在该函数中的位置不能随便放,否则可能会block住
roomStore
.
stage
=
RoomStage
.
DUEL_START
;
}
matStore
.
initInfo
.
set
(
0
,
{
life
:
start
.
life1
,
...
...
src/service/mora/selectTp.ts
View file @
332f394c
import
{
ygopro
}
from
"
@/api
"
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
RoomStage
,
roomStore
}
from
"
@/stores
"
;
import
{
RoomStage
,
roomStore
,
SideStage
,
sideStore
}
from
"
@/stores
"
;
export
default
function
handleSelectTp
(
_
:
ygopro
.
YgoStocMsg
)
{
roomStore
.
stage
=
RoomStage
.
TP_SELECTING
;
eventbus
.
emit
(
Task
.
Tp
);
if
(
sideStore
.
stage
!==
SideStage
.
NONE
)
{
sideStore
.
stage
=
SideStage
.
TP_SELECTING
;
}
else
{
roomStore
.
stage
=
RoomStage
.
TP_SELECTING
;
eventbus
.
emit
(
Task
.
Tp
);
}
}
src/service/onSocketMessage.ts
View file @
332f394c
...
...
@@ -20,13 +20,23 @@ import handleHsPlayerEnter from "./room/hsPlayerEnter";
import
handleHsWatchChange
from
"
./room/hsWatchChange
"
;
import
handleJoinGame
from
"
./room/joinGame
"
;
import
handleTypeChange
from
"
./room/typeChange
"
;
import
{
handleChangeSide
}
from
"
./side/changeSide
"
;
import
{
handleWaitingSide
}
from
"
./side/waitingSide
"
;
/*
* 先将从长连接中读取到的二进制数据通过Adapter转成protobuf结构体,
* 然后再分发到各个处理函数中去处理。
*
* */
let
animation
:
Promise
<
void
>
=
Promise
.
resolve
();
export
default
async
function
handleSocketMessage
(
e
:
MessageEvent
)
{
// 确保按序执行
animation
=
animation
.
then
(()
=>
_handle
(
e
));
}
async
function
_handle
(
e
:
MessageEvent
)
{
const
packet
=
YgoProPacket
.
deserialize
(
e
.
data
);
const
pb
=
adaptStoc
(
packet
);
...
...
@@ -93,6 +103,14 @@ export default async function handleSocketMessage(e: MessageEvent) {
await
handleErrorMsg
(
pb
.
stoc_error_msg
);
break
;
}
case
"
stoc_change_side
"
:
{
handleChangeSide
(
pb
.
stoc_change_side
);
break
;
}
case
"
stoc_waiting_side
"
:
{
handleWaitingSide
(
pb
.
stoc_waiting_side
);
break
;
}
default
:
{
console
.
log
(
packet
);
...
...
src/service/onSocketOpen.ts
View file @
332f394c
...
...
@@ -23,6 +23,6 @@ export default function handleSocketOpen(
ws
.
binaryType
=
"
arraybuffer
"
;
sendPlayerInfo
(
ws
,
player
);
sendJoinGame
(
ws
,
NeosConfig
.
version
,
passWd
);
// todo: version use config
sendJoinGame
(
ws
,
NeosConfig
.
version
,
passWd
);
}
}
src/service/room/duelStart.ts
View file @
332f394c
import
{
ygopro
}
from
"
@/api
"
;
import
{
RoomStage
,
roomStore
}
from
"
@/stores
"
;
import
{
RoomStage
,
roomStore
,
SideStage
,
sideStore
}
from
"
@/stores
"
;
export
default
function
handleDuelStart
(
_pb
:
ygopro
.
YgoStocMsg
)
{
roomStore
.
stage
=
RoomStage
.
MORA
;
if
(
sideStore
.
stage
!==
SideStage
.
NONE
)
{
sideStore
.
stage
=
SideStage
.
SIDE_CHANGED
;
}
else
{
roomStore
.
stage
=
RoomStage
.
MORA
;
}
}
src/service/side/changeSide.ts
0 → 100644
View file @
332f394c
import
{
ygopro
}
from
"
@/api
"
;
import
{
SideStage
,
sideStore
}
from
"
@/stores
"
;
export
function
handleChangeSide
(
_
:
ygopro
.
StocChangeSide
)
{
sideStore
.
stage
=
SideStage
.
SIDE_CHANGING
;
}
src/service/side/waitingSide.ts
0 → 100644
View file @
332f394c
import
{
ygopro
}
from
"
@/api
"
;
import
{
SideStage
,
sideStore
}
from
"
@/stores
"
;
export
function
handleWaitingSide
(
_
:
ygopro
.
StocWaitingSide
)
{
sideStore
.
stage
=
SideStage
.
WAITING
;
}
src/stores/index.ts
View file @
332f394c
...
...
@@ -7,6 +7,7 @@ export * from "./matStore";
export
*
from
"
./placeStore
"
;
export
*
from
"
./replayStore
"
;
export
*
from
"
./roomStore
"
;
export
*
from
"
./sideStore
"
;
import
{
devtools
}
from
"
valtio/utils
"
;
...
...
@@ -21,6 +22,7 @@ import { matStore } from "./matStore";
import
{
placeStore
}
from
"
./placeStore
"
;
import
{
replayStore
}
from
"
./replayStore
"
;
import
{
roomStore
}
from
"
./roomStore
"
;
import
{
sideStore
}
from
"
./sideStore
"
;
const
{
DEV
}
=
useEnv
();
...
...
@@ -33,6 +35,7 @@ devtools(accountStore, { name: "account", enabled: DEV });
devtools
(
roomStore
,
{
name
:
"
room
"
,
enabled
:
DEV
});
devtools
(
deckStore
,
{
name
:
"
deck
"
,
enabled
:
DEV
});
devtools
(
initStore
,
{
name
:
"
init
"
,
enabled
:
DEV
});
devtools
(
sideStore
,
{
name
:
"
side
"
,
enabled
:
DEV
});
// 重置`Store`
export
const
resetUniverse
=
()
=>
{
...
...
@@ -43,4 +46,12 @@ export const resetUniverse = () => {
placeStore
.
reset
();
replayStore
.
reset
();
roomStore
.
reset
();
sideStore
.
reset
();
};
// 重置决斗相关的`Store`
export
const
resetDuel
=
()
=>
{
cardStore
.
reset
();
matStore
.
reset
();
placeStore
.
reset
();
};
src/stores/sideStore.ts
0 → 100644
View file @
332f394c
import
{
proxy
}
from
"
valtio
"
;
import
{
IDeck
}
from
"
./deckStore
"
;
import
{
type
NeosStore
}
from
"
./shared
"
;
export
enum
SideStage
{
NONE
=
0
,
// 没有进入SIDE阶段
SIDE_CHANGING
=
1
,
// 正在更换副卡组
SIDE_CHANGED
=
2
,
// 副卡组更换完毕
TP_SELECTING
=
5
,
// 正在选边
TP_SELECTED
=
6
,
// 选边完成
DUEL_START
=
7
,
// 决斗开始
WAITING
=
8
,
// 观战者等待双方玩家
}
const
emptyDeck
:
IDeck
=
{
deckName
:
""
,
main
:
[],
extra
:
[],
side
:
[]
};
class
SideStore
implements
NeosStore
{
stage
:
SideStage
=
SideStage
.
NONE
;
deck
:
IDeck
=
emptyDeck
;
reset
():
void
{
this
.
stage
=
SideStage
.
NONE
;
this
.
deck
=
emptyDeck
;
}
}
export
const
sideStore
=
proxy
(
new
SideStore
());
src/ui/BuildDeck/index.tsx
View file @
332f394c
...
...
@@ -147,7 +147,7 @@ export const Component: React.FC = () => {
Component
.
displayName
=
"
Build
"
;
/** 正在编辑的卡组 */
const
DeckEditor
:
React
.
FC
<
{
export
const
DeckEditor
:
React
.
FC
<
{
deck
:
IDeck
;
onClear
:
()
=>
void
;
onReset
:
()
=>
void
;
...
...
src/ui/Duel/Main.tsx
View file @
332f394c
import
React
from
"
react
"
;
import
React
,
{
useEffect
}
from
"
react
"
;
import
{
resetUniverse
}
from
"
@/stores
"
;
import
{
ChangeSideModal
,
TpModal
}
from
"
../Side
"
;
import
{
Alert
,
AnnounceModal
,
...
...
@@ -18,6 +21,13 @@ import {
import
{
LifeBar
,
Mat
,
Menu
,
Underlying
}
from
"
./PlayMat
"
;
export
const
Component
:
React
.
FC
=
()
=>
{
useEffect
(()
=>
{
return
()
=>
{
// Duel组件卸载的时候初始化一些store
resetUniverse
();
};
},
[]);
return
(
<>
<
Underlying
/>
...
...
@@ -37,6 +47,8 @@ export const Component: React.FC = () => {
<
AnnounceModal
/>
<
SimpleSelectCardsModal
/>
<
EndModal
/>
<
ChangeSideModal
/>
<
TpModal
/>
</>
);
};
...
...
src/ui/Duel/Message/EndModal/index.tsx
View file @
332f394c
import
React
,
{
CSSProperties
}
from
"
react
"
;
import
{
useNavigate
}
from
"
react-router-dom
"
;
import
{
proxy
,
useSnapshot
}
from
"
valtio
"
;
import
{
fetchStrings
,
Region
}
from
"
@/api
"
;
import
{
matStore
,
replayStore
,
reset
Universe
}
from
"
@/stores
"
;
import
{
matStore
,
replayStore
,
reset
Duel
}
from
"
@/stores
"
;
import
{
NeosModal
}
from
"
../NeosModal
"
;
import
styles
from
"
./index.module.scss
"
;
...
...
@@ -25,11 +24,10 @@ export const EndModal: React.FC = () => {
const
{
isOpen
,
isWin
,
reason
}
=
useSnapshot
(
localStore
);
const
{
isReplay
}
=
useSnapshot
(
matStore
);
const
navigate
=
useNavigate
();
const
onReturn
=
()
=>
{
reset
Universe
();
reset
Duel
();
rs
();
navigate
(
"
/match
"
);
// TODO: 这里暂时不自动跳转,决斗结束后让玩家自己手动选择回到主页
};
return
(
...
...
src/ui/Duel/PlayMat/Mat/index.tsx
View file @
332f394c
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
cardStore
}
from
"
@/stores
"
;
import
{
Bg
}
from
"
../Bg
"
;
...
...
@@ -21,7 +23,8 @@ export const Mat: React.FC = () => {
};
const
Cards
:
React
.
FC
=
()
=>
{
const
length
=
cardStore
.
inner
.
length
;
const
{
inner
}
=
useSnapshot
(
cardStore
);
const
length
=
inner
.
length
;
return
(
<>
{
Array
.
from
({
length
}).
map
((
_
,
i
)
=>
(
...
...
src/ui/Match/MatchModal.tsx
View file @
332f394c
import
{
Button
,
Input
,
Modal
}
from
"
antd
"
;
import
{
App
,
Button
,
Input
,
Modal
}
from
"
antd
"
;
import
React
,
{
ChangeEvent
,
useEffect
,
useState
}
from
"
react
"
;
import
{
useNavigate
}
from
"
react-router-dom
"
;
import
{
proxy
,
useSnapshot
}
from
"
valtio
"
;
...
...
@@ -7,7 +7,7 @@ import { useConfig } from "@/config";
import
{
accountStore
,
roomStore
}
from
"
@/stores
"
;
import
styles
from
"
./MatchModal.module.scss
"
;
import
{
init
}
from
"
./util
"
;
import
{
connectSrvpro
}
from
"
./util
"
;
const
NeosConfig
=
useConfig
();
const
serverConfig
=
NeosConfig
.
servers
;
...
...
@@ -27,6 +27,7 @@ const defaultProps: Props = {
export
const
matchStore
=
proxy
<
Props
>
(
defaultProps
);
export
const
MatchModal
:
React
.
FC
=
({})
=>
{
const
{
message
}
=
App
.
useApp
();
const
{
open
}
=
useSnapshot
(
matchStore
);
const
{
user
}
=
useSnapshot
(
accountStore
);
const
{
joined
,
errorMsg
}
=
useSnapshot
(
roomStore
);
...
...
@@ -50,7 +51,7 @@ export const MatchModal: React.FC = ({}) => {
const
handleSubmit
=
async
()
=>
{
setConfirmLoading
(
true
);
await
init
({
player
,
ip
:
server
,
passWd
:
passwd
});
await
connectSrvpro
({
player
,
ip
:
server
,
passWd
:
passwd
});
};
useEffect
(()
=>
{
...
...
@@ -70,7 +71,7 @@ export const MatchModal: React.FC = ({}) => {
useEffect
(()
=>
{
// 出现错误
if
(
errorMsg
!==
undefined
&&
errorMsg
!==
""
)
{
alert
(
errorMsg
);
message
.
error
(
errorMsg
);
setConfirmLoading
(
false
);
roomStore
.
errorMsg
=
undefined
;
}
...
...
src/ui/Match/ReplayModal.tsx
View file @
332f394c
...
...
@@ -6,7 +6,7 @@ import { proxy, useSnapshot } from "valtio";
import
{
matStore
}
from
"
@/stores
"
;
import
{
Uploader
}
from
"
../Shared
"
;
import
{
init
}
from
"
./util
"
;
import
{
connectSrvpro
}
from
"
./util
"
;
const
localStore
=
proxy
({
open
:
false
,
...
...
@@ -45,7 +45,7 @@ export const ReplayModal: React.FC = () => {
// FIXME: 这样写应该不对,有空来修
window
.
myExtraDeckCodes
=
[];
await
init
({
await
connectSrvpro
({
ip
:
""
,
player
:
""
,
passWd
:
""
,
...
...
src/ui/Match/index.tsx
View file @
332f394c
import
{
EditOutlined
,
LoadingOutlined
,
PlayCircleOutlined
,
SettingFilled
,
}
from
"
@ant-design/icons
"
;
import
{
Button
,
Space
}
from
"
antd
"
;
import
{
App
,
Button
,
Space
}
from
"
antd
"
;
import
{
useEffect
,
useState
}
from
"
react
"
;
import
{
useNavigate
}
from
"
react-router-dom
"
;
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
match
}
from
"
@/api
"
;
import
{
useConfig
}
from
"
@/config
"
;
import
{
accountStore
,
deckStore
,
IDeck
,
roomStore
}
from
"
@/stores
"
;
import
{
Background
,
IconFont
,
Select
}
from
"
@/ui/Shared
"
;
...
...
@@ -15,24 +17,69 @@ import { Background, IconFont, Select } from "@/ui/Shared";
import
styles
from
"
./index.module.scss
"
;
import
{
MatchModal
,
matchStore
}
from
"
./MatchModal
"
;
import
{
ReplayModal
,
replayOpen
}
from
"
./ReplayModal
"
;
import
{
init
}
from
"
./util
"
;
import
{
connectSrvpro
}
from
"
./util
"
;
const
NeosConfig
=
useConfig
();
export
const
Component
:
React
.
FC
=
()
=>
{
const
{
message
}
=
App
.
useApp
();
const
serverList
=
NeosConfig
.
servers
;
const
[
server
,
setServer
]
=
useState
(
`
${
serverList
[
0
].
ip
}
:
${
serverList
[
0
].
port
}
`
,
);
const
{
decks
}
=
useSnapshot
(
deckStore
);
const
[
deck
,
setDeck
]
=
useState
<
IDeck
>
(
JSON
.
parse
(
JSON
.
stringify
(
decks
[
0
])));
const
{
user
}
=
useSnapshot
(
accountStore
)
;
const
user
=
accountStore
.
user
;
const
{
joined
}
=
useSnapshot
(
roomStore
);
const
[
singleLoading
,
setSingleLoading
]
=
useState
(
false
);
// 单人模式的loading状态
const
[
matchLoading
,
setMatchLoading
]
=
useState
(
false
);
const
navigate
=
useNavigate
();
// 竞技匹配
const
onCompetitiveMatch
=
()
=>
message
.
error
(
"
暂未开放,敬请期待
"
);
// 娱乐匹配
const
onEntertainMatch
=
async
()
=>
{
if
(
!
user
)
{
message
.
error
(
"
请先登录萌卡账号
"
);
}
else
{
setMatchLoading
(
true
);
const
matchInfo
=
await
match
(
user
.
username
,
user
.
external_id
);
if
(
matchInfo
)
{
await
connectSrvpro
({
ip
:
matchInfo
.
address
+
"
:
"
+
(
matchInfo
.
port
+
1
),
player
:
user
.
username
,
passWd
:
matchInfo
.
password
,
});
}
else
{
message
.
error
(
"
匹配失败T_T
"
);
}
}
};
// 单人模式
const
onAIMatch
=
async
()
=>
{
setSingleLoading
(
true
);
// 初始化,然后等待后端通知成功加入房间后跳转页面
await
connectSrvpro
({
ip
:
server
,
player
:
user
?.
name
??
"
Guest
"
,
passWd
:
"
AI
"
,
});
};
// 自定义房间
const
onCustomRoom
=
()
=>
(
matchStore
.
open
=
true
);
// 观战列表
const
onWatchList
=
()
=>
message
.
error
(
"
开发中,敬请期待
"
);
useEffect
(()
=>
{
// 人机对战跳转
if
(
joined
)
{
setSingleLoading
(
false
);
setMatchLoading
(
false
);
navigate
(
`/waitroom`
);
}
},
[
joined
]);
...
...
@@ -68,7 +115,7 @@ export const Component: React.FC = () => {
if
(
item
)
{
setDeck
(
item
);
}
else
{
alert
(
`Deck ${value} not found`
);
message
.
error
(
`Deck ${value} not found`
);
}
}
}
options=
{
decks
.
map
((
deck
)
=>
({
...
...
@@ -90,34 +137,37 @@ export const Component: React.FC = () => {
title=
"竞技匹配"
desc=
"与天梯其他数万名玩家激战,追求胜利登顶最强。每月最后一天晚上10点结算成绩,获取奖励与公布排名。"
icon=
{
<
IconFont
type=
"icon-battle"
size=
{
32
}
/>
}
onClick=
{
()
=>
alert
(
"
开发中,敬请期待
"
)
}
onClick=
{
onCompetitiveMatch
}
/>
<
Mode
title=
"娱乐匹配"
desc=
"过去一周竞技匹配使用数最靠前的20个卡组被禁止使用。将胜负暂且搁置,尽情享受决斗的乐趣。"
icon=
{
<
IconFont
type=
"icon-coffee"
size=
{
28
}
/>
}
onClick=
{
()
=>
alert
(
"
开发中,敬请期待
"
)
}
icon=
{
matchLoading
?
(
<
LoadingOutlined
/>
)
:
(
<
IconFont
type=
"icon-coffee"
size=
{
28
}
/>
)
}
onClick=
{
onEntertainMatch
}
/>
<
Mode
title=
"单人模式"
desc=
"开启与AI的决斗,验证自己的卡组,或者只是打发时间。"
icon=
{
<
IconFont
type=
"icon-chip"
size=
{
26
}
/>
}
onClick=
{
async
()
=>
{
// TODO: 有时间可以做一个Loading
// 初始化,然后等待后端通知成功加入房间后跳转页面
await
init
({
ip
:
server
,
player
:
user
?.
name
??
"
Guest
"
,
passWd
:
"
AI
"
,
});
}
}
icon=
{
singleLoading
?
(
<
LoadingOutlined
/>
)
:
(
<
IconFont
type=
"icon-chip"
size=
{
26
}
/>
)
}
onClick=
{
onAIMatch
}
/>
<
Mode
title=
"自定义房间"
desc=
"创建双打TAG或自定义规则的房间,或与好友约战,甚至举办竞技比赛。"
icon=
{
<
SettingFilled
/>
}
onClick=
{
()
=>
(
matchStore
.
open
=
true
)
}
onClick=
{
onCustomRoom
}
/>
<
Mode
title=
"录像回放"
...
...
@@ -129,7 +179,7 @@ export const Component: React.FC = () => {
title=
"观战列表"
desc=
"观看MyCard上正在进行的决斗"
icon=
{
<
PlayCircleOutlined
/>
}
onClick=
{
()
=>
alert
(
"
开发中,敬请期待
"
)
}
onClick=
{
onWatchList
}
/>
</
div
>
</
div
>
...
...
src/ui/Match/util.ts
View file @
332f394c
...
...
@@ -7,8 +7,8 @@ import sqliteMiddleWare, { sqliteCmd } from "@/middleware/sqlite";
const
NeosConfig
=
useConfig
();
//
进行进入房间/回放前的一些初始化操作
export
const
init
=
async
(
params
:
{
//
连接SRVPRO服务
export
const
connectSrvpro
=
async
(
params
:
{
ip
:
string
;
player
:
string
;
passWd
:
string
;
...
...
src/ui/Side/ChangeSideModal/index.tsx
0 → 100644
View file @
332f394c
import
{
App
,
Button
,
Modal
}
from
"
antd
"
;
import
React
,
{
useEffect
}
from
"
react
"
;
import
{
DndProvider
}
from
"
react-dnd
"
;
import
{
HTML5Backend
}
from
"
react-dnd-html5-backend
"
;
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
CardMeta
,
sendUpdateDeck
}
from
"
@/api
"
;
import
{
roomStore
,
SideStage
,
sideStore
}
from
"
@/stores
"
;
import
{
DeckEditor
}
from
"
../../BuildDeck
"
;
import
{
editDeckStore
}
from
"
../../BuildDeck/store
"
;
import
{
iDeckToEditingDeck
}
from
"
../../BuildDeck/utils
"
;
import
{
Background
}
from
"
../../Shared
"
;
export
const
ChangeSideModal
:
React
.
FC
=
()
=>
{
const
{
message
}
=
App
.
useApp
();
const
{
deckName
,
main
,
extra
,
side
}
=
useSnapshot
(
editDeckStore
);
const
{
stage
}
=
useSnapshot
(
sideStore
);
const
{
errorMsg
}
=
useSnapshot
(
roomStore
);
const
cardMeta2Id
=
(
meta
:
CardMeta
)
=>
meta
.
id
;
const
handleSummit
=
()
=>
{
const
newDeck
=
{
deckName
:
deckName
,
main
:
main
.
map
(
cardMeta2Id
),
extra
:
extra
.
map
(
cardMeta2Id
),
side
:
side
.
map
(
cardMeta2Id
),
};
sendUpdateDeck
(
newDeck
);
editDeckStore
.
edited
=
false
;
};
useEffect
(()
=>
{
if
(
stage
===
SideStage
.
SIDE_CHANGED
)
{
message
.
info
(
"
副卡组更换成功,请耐心等待其他玩家更换卡组
"
);
}
},
[
stage
]);
useEffect
(()
=>
{
if
(
errorMsg
!==
undefined
&&
errorMsg
!==
""
)
{
message
.
error
(
errorMsg
);
roomStore
.
errorMsg
=
undefined
;
}
},
[
errorMsg
]);
return
(
<
Modal
title=
"请选择更换副卡组"
open=
{
stage
===
SideStage
.
SIDE_CHANGING
||
stage
===
SideStage
.
SIDE_CHANGED
}
width=
{
700
}
closable=
{
false
}
footer=
{
<
Button
disabled=
{
stage
>
SideStage
.
SIDE_CHANGING
}
onClick=
{
handleSummit
}
>
副卡组更换完毕
</
Button
>
}
>
<
DndProvider
backend=
{
HTML5Backend
}
>
<
Background
/>
<
DeckEditor
deck=
{
sideStore
.
deck
}
onClear=
{
()
=>
message
.
error
(
"
对局中清空卡组不怕找不回来吗?!
"
)
}
onSave=
{
()
=>
message
.
error
(
"
点击右下角按钮确认副卡组更换完毕
"
)
}
onReset=
{
async
()
=>
{
editDeckStore
.
set
(
await
iDeckToEditingDeck
(
sideStore
.
deck
));
}
}
/>
</
DndProvider
>
</
Modal
>
);
};
src/ui/Side/TpModal/index.module.scss
0 → 100644
View file @
332f394c
.container
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
flex-direction
:
row
;
gap
:
80px
;
button
{
width
:
100px
;
height
:
50px
;
font-size
:
16px
;
}
}
src/ui/Side/TpModal/index.tsx
0 → 100644
View file @
332f394c
import
{
Button
,
Modal
}
from
"
antd
"
;
import
React
from
"
react
"
;
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
sendTpResult
}
from
"
@/api
"
;
import
{
SideStage
,
sideStore
}
from
"
@/stores
"
;
import
styles
from
"
./index.module.scss
"
;
export
const
TpModal
:
React
.
FC
=
()
=>
{
const
{
stage
}
=
useSnapshot
(
sideStore
);
return
(
<
Modal
centered
open=
{
stage
===
SideStage
.
TP_SELECTING
}
footer=
{
<></>
}
closable=
{
false
}
>
<
div
className=
{
styles
.
container
}
>
<
Button
onClick=
{
()
=>
{
sendTpResult
(
true
);
sideStore
.
stage
=
SideStage
.
TP_SELECTED
;
}
}
>
先手
</
Button
>
<
Button
onClick=
{
()
=>
{
sendTpResult
(
false
);
sideStore
.
stage
=
SideStage
.
TP_SELECTED
;
}
}
>
后手
</
Button
>
</
div
>
</
Modal
>
);
};
src/ui/Side/index.tsx
0 → 100644
View file @
332f394c
export
*
from
"
./ChangeSideModal
"
;
export
*
from
"
./TpModal
"
;
src/ui/WaitRoom/Popover.tsx
View file @
332f394c
...
...
@@ -68,7 +68,9 @@ export const MoraPopover: React.FC<
};
export
const
TpPopover
:
React
.
FC
<
React
.
PropsWithChildren
<
{
onSelect
?:
(
result
:
Tp
)
=>
void
}
>
React
.
PropsWithChildren
<
{
onSelect
?:
(
result
:
Tp
)
=>
void
;
}
>
>
=
({
children
,
onSelect
})
=>
{
const
[
open
,
setOpen
]
=
useState
(
false
);
...
...
src/ui/WaitRoom/index.tsx
View file @
332f394c
...
...
@@ -14,7 +14,7 @@ import {
import
socketMiddleWare
,
{
socketCmd
}
from
"
@/middleware/socket
"
;
import
PlayerState
=
ygopro
.
StocHsPlayerChange
.
State
;
import
SelfType
=
ygopro
.
StocTypeChange
.
SelfType
;
import
{
Avatar
,
Button
,
Skeleton
,
Space
}
from
"
antd
"
;
import
{
A
pp
,
A
vatar
,
Button
,
Skeleton
,
Space
}
from
"
antd
"
;
import
classNames
from
"
classnames
"
;
import
{
useEffect
,
useState
}
from
"
react
"
;
import
{
useNavigate
}
from
"
react-router-dom
"
;
...
...
@@ -29,6 +29,7 @@ import {
resetUniverse
,
RoomStage
,
roomStore
,
sideStore
,
}
from
"
@/stores
"
;
import
{
Background
,
IconFont
,
Select
,
SpecialButton
}
from
"
@/ui/Shared
"
;
...
...
@@ -39,11 +40,13 @@ import { Mora, MoraPopover, Tp, TpPopover } from "./Popover";
const
NeosConfig
=
useConfig
();
export
const
Component
:
React
.
FC
=
()
=>
{
const
{
message
}
=
App
.
useApp
();
const
{
user
}
=
useSnapshot
(
accountStore
);
const
[
collapsed
,
setCollapsed
]
=
useState
(
false
);
const
{
decks
}
=
useSnapshot
(
deckStore
);
const
[
deck
,
setDeck
]
=
useState
<
IDeck
>
(
JSON
.
parse
(
JSON
.
stringify
(
decks
[
0
])));
const
room
=
useSnapshot
(
roomStore
);
const
{
errorMsg
}
=
room
;
const
me
=
room
.
getMePlayer
();
const
op
=
room
.
getOpPlayer
();
const
navigate
=
useNavigate
();
...
...
@@ -55,6 +58,13 @@ export const Component: React.FC = () => {
// TODO: 重置房间状态(也可能是在这个页面的loader之中重置,就看是进房间重置还是离开时重置,可能需要考虑意外离开的情况)
}
},
[
room
.
stage
]);
useEffect
(()
=>
{
// 出现错误
if
(
errorMsg
!==
undefined
&&
errorMsg
!==
""
)
{
message
.
error
(
errorMsg
);
roomStore
.
errorMsg
=
undefined
;
}
},
[
errorMsg
]);
return
(
<
div
...
...
@@ -75,7 +85,6 @@ export const Component: React.FC = () => {
<
Controller
onDeckChange=
{
(
deckName
:
string
)
=>
{
const
deck
=
deckStore
.
get
(
deckName
);
// 同步后端
if
(
deck
)
{
setDeck
(
deck
);
}
else
{
...
...
@@ -96,6 +105,9 @@ export const Component: React.FC = () => {
onClick=
{
()
=>
{
if
(
me
?.
state
===
PlayerState
.
NO_READY
)
{
sendUpdateDeck
(
deck
);
// 设置side里面的卡组
sideStore
.
deck
=
deck
;
// 设置额外卡组数据
window
.
myExtraDeckCodes
=
[...
deck
.
extra
];
sendHsReady
();
}
else
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment