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
6d60d4ee
Commit
6d60d4ee
authored
May 11, 2024
by
Chunchi Che
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
优化一些交互,玩家融合/超量/链接/同调召唤的时候可以通过点击场上的卡来完成操作
parent
76de28e9
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
202 additions
and
52 deletions
+202
-52
src/service/duel/becomeTarget.ts
src/service/duel/becomeTarget.ts
+1
-1
src/service/duel/chainEnd.ts
src/service/duel/chainEnd.ts
+1
-1
src/service/duel/reloadField.ts
src/service/duel/reloadField.ts
+5
-1
src/service/duel/selectUnselectCard.ts
src/service/duel/selectUnselectCard.ts
+56
-20
src/service/duel/start.ts
src/service/duel/start.ts
+5
-1
src/stores/cardStore.ts
src/stores/cardStore.ts
+6
-1
src/stores/matStore/store.ts
src/stores/matStore/store.ts
+14
-0
src/stores/matStore/types.ts
src/stores/matStore/types.ts
+7
-0
src/ui/Duel/PlayMat/Card/index.module.scss
src/ui/Duel/PlayMat/Card/index.module.scss
+11
-2
src/ui/Duel/PlayMat/Card/index.tsx
src/ui/Duel/PlayMat/Card/index.tsx
+28
-6
src/ui/Duel/PlayMat/Menu/index.module.scss
src/ui/Duel/PlayMat/Menu/index.module.scss
+10
-0
src/ui/Duel/PlayMat/Menu/index.tsx
src/ui/Duel/PlayMat/Menu/index.tsx
+26
-19
src/ui/Duel/utils/clearAllIdleInteractivities.ts
src/ui/Duel/utils/clearAllIdleInteractivities.ts
+7
-0
src/ui/Duel/utils/clearSelectInfo.ts
src/ui/Duel/utils/clearSelectInfo.ts
+23
-0
src/ui/Duel/utils/index.ts
src/ui/Duel/utils/index.ts
+2
-0
No files found.
src/service/duel/becomeTarget.ts
View file @
6d60d4ee
...
...
@@ -10,7 +10,7 @@ export default (becomeTarget: ygopro.StocGameMessage.MsgBecomeTarget) => {
);
if
(
target
)
{
console
.
info
(
`
${
target
.
meta
.
text
.
name
}
become target`
);
target
.
selec
ted
=
true
;
target
.
targe
ted
=
true
;
}
else
{
console
.
warn
(
`<BecomeTarget>target from
${
location
}
is null`
);
}
...
...
src/service/duel/chainEnd.ts
View file @
6d60d4ee
...
...
@@ -23,6 +23,6 @@ export default (_chainEnd: ygopro.StocGameMessage.MsgChainEnd) => {
//
// TODO: 这里每次都要全部遍历一遍,后续可以优化下
for
(
const
card
of
cardStore
.
inner
)
{
card
.
selec
ted
=
false
;
card
.
targe
ted
=
false
;
}
};
src/service/duel/reloadField.ts
View file @
6d60d4ee
...
...
@@ -35,7 +35,11 @@ export default (field: MsgReloadField) => {
idleInteractivities
:
[],
meta
:
{
id
:
0
,
data
:
{},
text
:
{}
},
isToken
:
false
,
selected
:
false
,
targeted
:
false
,
selectInfo
:
{
selectable
:
false
,
selected
:
false
,
},
}),
),
)
...
...
src/service/duel/selectUnselectCard.ts
View file @
6d60d4ee
import
{
ygopro
}
from
"
@/api
"
;
import
{
cardStore
,
matStore
}
from
"
@/stores
"
;
import
{
displaySelectActionsModal
}
from
"
@/ui/Duel/Message/SelectActionsModal
"
;
import
{
fetchCheckCardMeta
}
from
"
../utils
"
;
type
MsgSelectUnselectCard
=
ygopro
.
StocGameMessage
.
MsgSelectUnselectCard
;
const
{
MZONE
,
SZONE
,
HAND
}
=
ygopro
.
CardZone
;
export
default
async
({
finishable
,
...
...
@@ -12,24 +14,58 @@ export default async ({
selectable_cards
:
selectableCards
,
selected_cards
:
selectedCards
,
}:
MsgSelectUnselectCard
)
=>
{
const
{
selecteds
:
selecteds1
,
mustSelects
:
mustSelect1
,
selectables
:
selectable1
,
}
=
await
fetchCheckCardMeta
(
selectableCards
);
const
{
selecteds
:
selecteds2
,
mustSelects
:
mustSelect2
,
selectables
:
selectable2
,
}
=
await
fetchCheckCardMeta
(
selectedCards
,
true
);
await
displaySelectActionsModal
({
finishable
,
cancelable
,
min
:
min
,
max
:
max
,
single
:
true
,
selecteds
:
[...
selecteds1
,
...
selecteds2
],
mustSelects
:
[...
mustSelect1
,
...
mustSelect2
],
selectables
:
[...
selectable1
,
...
selectable2
],
});
if
(
selectableCards
.
concat
(
selectedCards
)
.
find
((
info
)
=>
!
isOnField
(
info
.
location
))
===
undefined
)
{
// 所有可选卡和已选卡都是在场上或手牌
// 通过让玩家点击场上的卡来进行选择
for
(
const
info
of
selectableCards
)
{
const
card
=
cardStore
.
find
(
info
.
location
);
if
(
card
)
{
matStore
.
selectUnselectInfo
.
selectableList
.
push
(
info
.
location
);
card
.
selectInfo
.
selectable
=
true
;
card
.
selectInfo
.
response
=
info
.
response
;
}
}
for
(
const
info
of
selectedCards
)
{
const
card
=
cardStore
.
find
(
info
.
location
);
if
(
card
)
{
matStore
.
selectUnselectInfo
.
selectedList
.
push
(
info
.
location
);
card
.
selectInfo
.
selected
=
true
;
card
.
selectInfo
.
response
=
info
.
response
;
}
}
matStore
.
selectUnselectInfo
.
finishable
=
finishable
;
matStore
.
selectUnselectInfo
.
cancelable
=
cancelable
;
}
else
{
// 有一些卡不在场上或手牌,因此无法通过点击卡片来选择
// 这里通过让玩家点击Modal中的卡来进行选择
const
{
selecteds
:
selecteds1
,
mustSelects
:
mustSelect1
,
selectables
:
selectable1
,
}
=
await
fetchCheckCardMeta
(
selectableCards
);
const
{
selecteds
:
selecteds2
,
mustSelects
:
mustSelect2
,
selectables
:
selectable2
,
}
=
await
fetchCheckCardMeta
(
selectedCards
,
true
);
await
displaySelectActionsModal
({
finishable
,
cancelable
,
min
:
min
,
max
:
max
,
single
:
true
,
selecteds
:
[...
selecteds1
,
...
selecteds2
],
mustSelects
:
[...
mustSelect1
,
...
mustSelect2
],
selectables
:
[...
selectable1
,
...
selectable2
],
});
}
};
function
isOnField
(
location
:
ygopro
.
CardLocation
):
boolean
{
return
[
MZONE
,
SZONE
,
HAND
].
includes
(
location
.
zone
);
}
src/service/duel/start.ts
View file @
6d60d4ee
...
...
@@ -75,7 +75,11 @@ export default async (start: ygopro.StocGameMessage.MsgStart) => {
text
:
{},
},
isToken
:
!
((
i
+
1
)
%
3
),
selected
:
false
,
targeted
:
false
,
selectInfo
:
{
selectable
:
false
,
selected
:
false
,
},
}),
),
),
...
...
src/stores/cardStore.ts
View file @
6d60d4ee
...
...
@@ -16,7 +16,12 @@ export interface CardType {
idleInteractivities
:
Interactivity
<
number
>
[];
// IDLE状态下的互动信息
counters
:
{
[
type
:
number
]:
number
};
// 指示器
isToken
:
boolean
;
// 是否是token
selected
:
boolean
;
// 当前卡是否被选择成为效果的对象
targeted
:
boolean
;
// 当前卡是否被选择成为效果的对象
selectInfo
:
{
selectable
:
boolean
;
// 是否可以被选择
selected
:
boolean
;
// 是否已经被选择
response
?:
number
;
// 被选择时发送给服务器的值
};
}
class
CardStore
implements
NeosStore
{
...
...
src/stores/matStore/store.ts
View file @
6d60d4ee
...
...
@@ -83,6 +83,12 @@ const initialState: Omit<MatState, "reset"> = {
},
},
tossResult
:
undefined
,
selectUnselectInfo
:
{
finishable
:
false
,
cancelable
:
false
,
selectableList
:
[],
selectedList
:
[],
},
chainSetting
:
ChainSetting
.
CHAIN_SMART
,
duelEnd
:
false
,
// methods
...
...
@@ -101,6 +107,7 @@ class MatStore implements MatState, NeosStore {
unimplemented
=
initialState
.
unimplemented
;
handResults
=
initialState
.
handResults
;
tossResult
=
initialState
.
tossResult
;
selectUnselectInfo
=
initialState
.
selectUnselectInfo
;
duelEnd
=
initialState
.
duelEnd
;
// methods
isMe
=
initialState
.
isMe
;
...
...
@@ -122,6 +129,13 @@ class MatStore implements MatState, NeosStore {
this
.
unimplemented
=
0
;
this
.
handResults
.
me
=
0
;
this
.
handResults
.
op
=
0
;
this
.
tossResult
=
undefined
;
this
.
selectUnselectInfo
=
{
finishable
:
false
,
cancelable
:
false
,
selectableList
:
[],
selectedList
:
[],
};
this
.
duelEnd
=
false
;
}
}
...
...
src/stores/matStore/types.ts
View file @
6d60d4ee
...
...
@@ -34,6 +34,13 @@ export interface MatState {
tossResult
?:
string
;
// 骰子/硬币结果
selectUnselectInfo
:
{
finishable
:
boolean
;
// 是否可以完成选择
cancelable
:
boolean
;
// 是否可以取消当前选择
selectableList
:
ygopro
.
CardLocation
[];
// 记录当前可以选择的卡列表
selectedList
:
ygopro
.
CardLocation
[];
// 记录当前已经选择的卡列表
};
handResults
:
BothSide
<
HandResult
>
&
{
set
:
(
controller
:
number
,
result
:
HandResult
)
=>
void
;
};
// 猜拳结果
...
...
src/ui/Duel/PlayMat/Card/index.module.scss
View file @
6d60d4ee
...
...
@@ -102,8 +102,7 @@
}
}
.mat-card.glowing
.shadow
{
--shadow-color
:
#0099ff
;
.frame
{
display
:
block
!
important
;
background
:
var
(
--
shadow-color
)
!
important
;
border-radius
:
5px
;
...
...
@@ -111,6 +110,16 @@
transform
:
translateZ
(
calc
((
var
(
--
z
))
*
1px
+
0
.1px
));
}
.mat-card.glowing
.shadow
{
--shadow-color
:
#0099ff
;
@extend
.frame
;
}
.mat-card.shining
{
--shadow-color
:
#f4ff00
;
@extend
.frame
;
}
@keyframes
focus
{
0
%
{
filter
:
brightness
(
1
)
contrast
(
1
);
...
...
src/ui/Duel/PlayMat/Card/index.tsx
View file @
6d60d4ee
...
...
@@ -4,7 +4,7 @@ import classnames from "classnames";
import
React
,
{
type
CSSProperties
,
useEffect
,
useRef
,
useState
}
from
"
react
"
;
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
type
CardMeta
,
Region
}
from
"
@/api
"
;
import
{
type
CardMeta
,
Region
,
sendSelectMultiResponse
}
from
"
@/api
"
;
import
{
fetchStrings
,
getCardStr
,
...
...
@@ -21,7 +21,12 @@ import {
displayOptionModal
,
displaySimpleSelectCardsModal
,
}
from
"
../../Message
"
;
import
{
interactTypeToIcon
,
interactTypeToString
}
from
"
../../utils
"
;
import
{
clearAllIdleInteractivities
,
clearSelectInfo
,
interactTypeToIcon
,
interactTypeToString
,
}
from
"
../../utils
"
;
import
styles
from
"
./index.module.scss
"
;
import
{
attack
,
...
...
@@ -157,6 +162,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
// 单卡: 直接召唤/特殊召唤/...
const
card
=
cards
[
0
];
sendSelectIdleCmdResponse
(
getNonEffectResponse
(
action
,
card
));
clearAllIdleInteractivities
();
}
else
{
// 场地: 选择卡片
// TODO: hint
...
...
@@ -167,7 +173,10 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
response
:
getNonEffectResponse
(
action
,
card
),
})),
});
sendSelectIdleCmdResponse
(
option
[
0
].
response
!
);
if
(
option
.
length
>
0
)
{
sendSelectIdleCmdResponse
(
option
[
0
].
response
!
);
clearAllIdleInteractivities
();
}
}
},
}),
...
...
@@ -234,6 +243,16 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
const onClick = () =
>
{
const
onCardClick
=
(
card
:
CardType
)
=>
{
const
selectInfo
=
card
.
selectInfo
;
if
(
selectInfo
.
selectable
||
selectInfo
.
selected
)
{
if
(
selectInfo
.
response
!==
undefined
)
{
sendSelectMultiResponse
([
selectInfo
.
response
]);
clearSelectInfo
();
}
else
{
console
.
error
(
"
card is selectable but the response is undefined!
"
);
}
}
// 中央弹窗展示选中卡牌信息
// TODO: 同一张卡片,是否重复点击会关闭CardModal?
displayCardModal
(
card
);
...
...
@@ -274,7 +293,11 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
return
(
<
animated
.
div
className=
{
classnames
(
styles
[
"
mat-card
"
],
{
[
styles
.
glowing
]:
glowing
})
}
className=
{
classnames
(
styles
[
"
mat-card
"
],
{
/* 有可操作选项或者已被选中*/
[
styles
.
glowing
]:
glowing
||
snap
.
selectInfo
.
selected
,
[
styles
.
shining
]:
snap
.
selectInfo
.
selectable
,
// 可以被选中
})
}
style=
{
{
transform
:
to
(
...
...
@@ -313,13 +336,12 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
>
<
YgoCard
className=
{
styles
.
cover
}
// cardName={snap.meta.text.name}
code=
{
snap
.
code
===
0
?
snap
.
meta
.
id
:
snap
.
code
}
/>
<
YgoCard
className=
{
styles
.
back
}
isBack
/>
</
div
>
</
Dropdown
>
{
snap
.
selec
ted
?
<
div
className=
{
styles
.
streamer
}
/>
:
<></>
}
{
snap
.
targe
ted
?
<
div
className=
{
styles
.
streamer
}
/>
:
<></>
}
</
animated
.
div
>
);
});
...
...
src/ui/Duel/PlayMat/Menu/index.module.scss
View file @
6d60d4ee
...
...
@@ -15,3 +15,13 @@
:global
(
.ant-dropdown-menu-item
)
{
gap
:
0
.5rem
;
}
.select-manager
{
.btn
{
font-size
:
0
.9rem
;
}
.cancle
{
color
:
red
;
}
}
src/ui/Duel/PlayMat/Menu/index.tsx
View file @
6d60d4ee
...
...
@@ -2,7 +2,6 @@ import {
ArrowRightOutlined
,
CheckOutlined
,
CloseCircleFilled
,
LogoutOutlined
,
MessageFilled
,
StepForwardFilled
,
}
from
"
@ant-design/icons
"
;
...
...
@@ -16,29 +15,28 @@ import {
theme
,
Tooltip
,
}
from
"
antd
"
;
import
classNames
from
"
classnames
"
;
import
{
cloneElement
,
useEffect
,
useState
}
from
"
react
"
;
import
{
useNavigate
}
from
"
react-router-dom
"
;
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
sendSelectBattleCmdResponse
,
sendSelectIdleCmdResponse
,
sendSelectSingleResponse
,
sendSurrender
,
ygopro
,
}
from
"
@/api
"
;
import
{
cardStore
,
ChainSetting
,
matStore
}
from
"
@/stores
"
;
import
{
ChainSetting
,
matStore
}
from
"
@/stores
"
;
import
{
IconFont
}
from
"
@/ui/Shared
"
;
import
styles
from
"
./index.module.scss
"
;
import
PhaseType
=
ygopro
.
StocGameMessage
.
MsgNewPhase
.
PhaseType
;
import
{
clearAllIdleInteractivities
,
clearSelectInfo
}
from
"
../../utils
"
;
import
{
openChatBox
}
from
"
../ChatBox
"
;
const
{
useToken
}
=
theme
;
const
clearAllIdleInteractivities
=
()
=>
{
for
(
const
card
of
cardStore
.
inner
)
{
card
.
idleInteractivities
=
[];
}
};
const
FINISH_CANCEL_RESPONSE
=
-
1
;
// PhaseType, 中文, response, 是否显示,是否禁用
const
initialPhaseBind
:
[
...
...
@@ -72,8 +70,6 @@ export const Menu = () => {
[],
);
const
navigate
=
useNavigate
();
useEffect
(()
=>
{
const
endResponse
=
[
PhaseType
.
BATTLE_START
,
...
...
@@ -154,10 +150,9 @@ export const Menu = () => {
const
globalDisable
=
!
matStore
.
isMe
(
currentPlayer
);
const
onExit
=
()
=>
navigate
(
"
/match
"
);
return
(
<
div
className=
{
styles
[
"
menu-container
"
]
}
>
<
SelectManager
/>
<
DropdownWithTitle
title=
"请选择要进入的阶段"
menu=
{
{
items
:
phaseSwitchItems
}
}
...
...
@@ -194,13 +189,6 @@ export const Menu = () => {
>
<
Button
icon=
{
<
CloseCircleFilled
/>
}
type=
"text"
></
Button
>
</
DropdownWithTitle
>
<
Tooltip
title=
"退出页面"
>
<
Button
icon=
{
<
LogoutOutlined
style=
{
{
color
:
"
red
"
}
}
/>
}
type=
"text"
onClick=
{
onExit
}
></
Button
>
</
Tooltip
>
</
div
>
);
};
...
...
@@ -256,3 +244,22 @@ const ChainIcon: React.FC<{ chainSetting: ChainSetting }> = ({
return
<
IconFont
type=
"icon-chain-broken"
/>;
}
};
const
SelectManager
:
React
.
FC
=
()
=>
{
const
{
finishable
,
cancelable
}
=
useSnapshot
(
matStore
.
selectUnselectInfo
);
const
onFinishOrCancel
=
()
=>
{
sendSelectSingleResponse
(
FINISH_CANCEL_RESPONSE
);
clearSelectInfo
();
};
return
(
<
div
className=
{
styles
[
"
select-manager
"
]
}
>
<
Button
className=
{
classNames
(
styles
.
btn
,
{
[
styles
.
cancle
]:
cancelable
})
}
disabled=
{
!
cancelable
&&
!
finishable
}
onClick=
{
onFinishOrCancel
}
>
{
finishable
?
"
完成选择
"
:
"
取消选择
"
}
</
Button
>
</
div
>
);
};
src/ui/Duel/utils/clearAllIdleInteractivities.ts
0 → 100644
View file @
6d60d4ee
import
{
cardStore
}
from
"
@/stores
"
;
export
function
clearAllIdleInteractivities
()
{
for
(
const
card
of
cardStore
.
inner
)
{
card
.
idleInteractivities
=
[];
}
}
src/ui/Duel/utils/clearSelectInfo.ts
0 → 100644
View file @
6d60d4ee
import
{
cardStore
,
matStore
}
from
"
@/stores
"
;
export
function
clearSelectInfo
()
{
const
selectUnselectInfo
=
matStore
.
selectUnselectInfo
;
for
(
const
location
of
selectUnselectInfo
.
selectableList
)
{
const
card
=
cardStore
.
find
(
location
);
if
(
card
)
{
card
.
selectInfo
.
selectable
=
false
;
card
.
selectInfo
.
response
=
undefined
;
}
}
for
(
const
location
of
selectUnselectInfo
.
selectedList
)
{
const
card
=
cardStore
.
find
(
location
);
if
(
card
)
{
card
.
selectInfo
.
selected
=
false
;
}
}
matStore
.
selectUnselectInfo
.
finishable
=
false
;
matStore
.
selectUnselectInfo
.
cancelable
=
false
;
matStore
.
selectUnselectInfo
.
selectableList
=
[];
matStore
.
selectUnselectInfo
.
selectedList
=
[];
}
src/ui/Duel/utils/index.ts
View file @
6d60d4ee
export
*
from
"
./clearAllIdleInteractivities
"
;
export
*
from
"
./clearSelectInfo
"
;
export
*
from
"
./groupBy
"
;
export
*
from
"
./interactTypeToStringIcon
"
;
export
*
from
"
./zip
"
;
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