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
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
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
love_飞影
Neos
Commits
e24f0887
Commit
e24f0887
authored
May 11, 2024
by
Chunchi Che
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'optimize/ui/interaction' into 'main'
优化一些交互,玩家融合/超量/链接/同调召唤的时候可以通过点击场上的卡来完成操作 See merge request
!368
parents
76de28e9
6d60d4ee
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 @
e24f0887
...
...
@@ -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 @
e24f0887
...
...
@@ -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 @
e24f0887
...
...
@@ -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 @
e24f0887
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 @
e24f0887
...
...
@@ -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 @
e24f0887
...
...
@@ -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 @
e24f0887
...
...
@@ -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 @
e24f0887
...
...
@@ -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 @
e24f0887
...
...
@@ -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 @
e24f0887
...
...
@@ -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 @
e24f0887
...
...
@@ -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 @
e24f0887
...
...
@@ -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 @
e24f0887
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 @
e24f0887
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 @
e24f0887
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