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
b1fba04e
Commit
b1fba04e
authored
Mar 31, 2024
by
Chunchi Che
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feat/ui/chain' into 'main'
添加连锁特效(第一期) See merge request
mycard/Neos!357
parents
89b0e9c9
a1f58c83
Pipeline
#26180
passed with stages
in 10 minutes and 5 seconds
Changes
9
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
204 additions
and
16 deletions
+204
-16
neos-assets/chain.png
neos-assets/chain.png
+0
-0
src/stores/placeStore.ts
src/stores/placeStore.ts
+2
-1
src/ui/Duel/Main.tsx
src/ui/Duel/Main.tsx
+2
-0
src/ui/Duel/PlayMat/Bg/index.tsx
src/ui/Duel/PlayMat/Bg/index.tsx
+43
-15
src/ui/Duel/PlayMat/HandChain/index.module.scss
src/ui/Duel/PlayMat/HandChain/index.module.scss
+18
-0
src/ui/Duel/PlayMat/HandChain/index.tsx
src/ui/Duel/PlayMat/HandChain/index.tsx
+33
-0
src/ui/Shared/Chain/index.module.scss
src/ui/Shared/Chain/index.module.scss
+60
-0
src/ui/Shared/Chain/index.tsx
src/ui/Shared/Chain/index.tsx
+45
-0
src/ui/Shared/index.ts
src/ui/Shared/index.ts
+1
-0
No files found.
neos-assets/chain.png
0 → 100644
View file @
b1fba04e
9.68 KB
src/stores/placeStore.ts
View file @
b1fba04e
...
@@ -20,7 +20,8 @@ const { MZONE, SZONE, HAND, GRAVE, REMOVED, EXTRA } = ygopro.CardZone;
...
@@ -20,7 +20,8 @@ const { MZONE, SZONE, HAND, GRAVE, REMOVED, EXTRA } = ygopro.CardZone;
export
interface
BlockState
{
export
interface
BlockState
{
interactivity
?:
PlaceInteractivity
;
// 互动性
interactivity
?:
PlaceInteractivity
;
// 互动性
disabled
:
boolean
;
// 是否被禁用
disabled
:
boolean
;
// 是否被禁用
chainIndex
:
number
[];
// 当前位置上的连锁序号。YGOPRO和MASTER DUEL的连锁都是和位置绑定的,因此在`PlaceStore`中记录连锁状态。
chainIndex
:
number
[]
/* 当前位置上的连锁序号。
YGOPRO和MASTER DUEL的连锁都是和位置绑定的,因此在`PlaceStore`中记录连锁状态。*/
;
}
}
const
genPLaces
=
(
n
:
number
):
BlockState
[]
=>
const
genPLaces
=
(
n
:
number
):
BlockState
[]
=>
...
...
src/ui/Duel/Main.tsx
View file @
b1fba04e
...
@@ -20,6 +20,7 @@ import {
...
@@ -20,6 +20,7 @@ import {
}
from
"
./Message
"
;
}
from
"
./Message
"
;
import
{
LifeBar
,
Mat
,
Menu
,
Underlying
}
from
"
./PlayMat
"
;
import
{
LifeBar
,
Mat
,
Menu
,
Underlying
}
from
"
./PlayMat
"
;
import
{
ChatBox
}
from
"
./PlayMat/ChatBox
"
;
import
{
ChatBox
}
from
"
./PlayMat/ChatBox
"
;
import
{
HandChain
}
from
"
./PlayMat/HandChain
"
;
export
const
Component
:
React
.
FC
=
()
=>
{
export
const
Component
:
React
.
FC
=
()
=>
{
const
{
stage
}
=
useSnapshot
(
sideStore
);
const
{
stage
}
=
useSnapshot
(
sideStore
);
...
@@ -59,6 +60,7 @@ export const Component: React.FC = () => {
...
@@ -59,6 +60,7 @@ export const Component: React.FC = () => {
<
SimpleSelectCardsModal
/>
<
SimpleSelectCardsModal
/>
<
EndModal
/>
<
EndModal
/>
<
ChatBox
/>
<
ChatBox
/>
<
HandChain
/>
</>
</>
);
);
};
};
...
...
src/ui/Duel/PlayMat/Bg/index.tsx
View file @
b1fba04e
...
@@ -9,20 +9,25 @@ import {
...
@@ -9,20 +9,25 @@ import {
type
PlaceInteractivity
,
type
PlaceInteractivity
,
placeStore
,
placeStore
,
}
from
"
@/stores
"
;
}
from
"
@/stores
"
;
import
{
BgChain
,
ChainProps
}
from
"
@/ui/Shared
"
;
import
styles
from
"
./index.module.scss
"
;
import
styles
from
"
./index.module.scss
"
;
const
{
MZONE
,
SZONE
,
EXTRA
,
GRAVE
,
REMOVED
}
=
ygopro
.
CardZone
;
const
BgBlock
:
React
.
FC
<
const
BgBlock
:
React
.
FC
<
React
.
HTMLProps
<
HTMLDivElement
>
&
{
React
.
HTMLProps
<
HTMLDivElement
>
&
{
disabled
?:
boolean
;
disabled
?:
boolean
;
highlight
?:
boolean
;
highlight
?:
boolean
;
glowing
?:
boolean
;
glowing
?:
boolean
;
chains
:
ChainProps
;
}
}
>
=
({
>
=
({
disabled
=
false
,
disabled
=
false
,
highlight
=
false
,
highlight
=
false
,
glowing
=
false
,
glowing
=
false
,
className
,
className
,
chains
,
...
rest
...
rest
})
=>
(
})
=>
(
<
div
<
div
...
@@ -34,6 +39,7 @@ const BgBlock: React.FC<
...
@@ -34,6 +39,7 @@ const BgBlock: React.FC<
>
>
{
<
DecoTriangles
/>
}
{
<
DecoTriangles
/>
}
{
<
DisabledCross
disabled=
{
disabled
}
/>
}
{
<
DisabledCross
disabled=
{
disabled
}
/>
}
{
<
BgChain
{
...
chains
}
/>
}
</
div
>
</
div
>
);
);
...
@@ -53,6 +59,8 @@ const BgExtraRow: React.FC<{
...
@@ -53,6 +59,8 @@ const BgExtraRow: React.FC<{
}
}
}
}
disabled=
{
meSnap
[
i
].
disabled
||
opSnap
[
i
].
disabled
}
disabled=
{
meSnap
[
i
].
disabled
||
opSnap
[
i
].
disabled
}
highlight=
{
!!
meSnap
[
i
].
interactivity
||
!!
opSnap
[
i
].
interactivity
}
highlight=
{
!!
meSnap
[
i
].
interactivity
||
!!
opSnap
[
i
].
interactivity
}
/* FIXME */
chains=
{
{
chains
:
meSnap
[
i
].
chainIndex
.
concat
(
opSnap
[
i
].
chainIndex
)
}
}
/>
/>
))
}
))
}
</
div
>
</
div
>
...
@@ -72,6 +80,7 @@ const BgRow: React.FC<{
...
@@ -72,6 +80,7 @@ const BgRow: React.FC<{
onClick=
{
()
=>
onBlockClick
(
snap
[
i
].
interactivity
)
}
onClick=
{
()
=>
onBlockClick
(
snap
[
i
].
interactivity
)
}
disabled=
{
snap
[
i
].
disabled
}
disabled=
{
snap
[
i
].
disabled
}
highlight=
{
!!
snap
[
i
].
interactivity
}
highlight=
{
!!
snap
[
i
].
interactivity
}
chains=
{
{
chains
:
snap
[
i
].
chainIndex
}
}
/>
/>
))
}
))
}
</
div
>
</
div
>
...
@@ -84,27 +93,46 @@ const BgOtherBlocks: React.FC<{ op?: boolean }> = ({ op }) => {
...
@@ -84,27 +93,46 @@ const BgOtherBlocks: React.FC<{ op?: boolean }> = ({ op }) => {
!!
cardStore
!!
cardStore
.
at
(
zone
,
meController
)
.
at
(
zone
,
meController
)
.
reduce
((
sum
,
c
)
=>
(
sum
+=
c
.
idleInteractivities
.
length
),
0
);
.
reduce
((
sum
,
c
)
=>
(
sum
+=
c
.
idleInteractivities
.
length
),
0
);
const
glowingExtra
=
judgeGlowing
(
ygopro
.
CardZone
.
EXTRA
);
const
glowingExtra
=
judgeGlowing
(
EXTRA
);
const
glowingGraveyard
=
judgeGlowing
(
ygopro
.
CardZone
.
GRAVE
);
const
glowingGraveyard
=
judgeGlowing
(
GRAVE
);
const
glowingBanish
=
judgeGlowing
(
ygopro
.
CardZone
.
REMOVED
);
const
glowingBanish
=
judgeGlowing
(
REMOVED
);
const
snap
=
useSnapshot
(
placeStore
.
inner
);
const
snap
=
useSnapshot
(
placeStore
.
inner
);
const
field
=
op
const
field
=
op
?
snap
[
SZONE
].
op
[
5
]
:
snap
[
SZONE
].
me
[
5
];
?
snap
[
ygopro
.
CardZone
.
SZONE
].
op
[
5
]
const
grave
=
op
?
snap
[
GRAVE
].
op
:
snap
[
GRAVE
].
me
;
:
snap
[
ygopro
.
CardZone
.
SZONE
].
me
[
5
];
const
removed
=
op
?
snap
[
REMOVED
].
op
:
snap
[
REMOVED
].
me
;
const
extra
=
op
?
snap
[
EXTRA
].
op
:
snap
[
EXTRA
].
me
;
const
genChains
=
(
states
:
Snapshot
<
BlockState
[]
>
)
=>
{
const
chains
:
number
[]
=
states
.
flatMap
((
state
)
=>
state
.
chainIndex
);
chains
.
sort
();
return
chains
;
};
return
(
return
(
<
div
className=
{
classnames
(
styles
[
"
other-blocks
"
],
{
[
styles
.
op
]:
op
})
}
>
<
div
className=
{
classnames
(
styles
[
"
other-blocks
"
],
{
[
styles
.
op
]:
op
})
}
>
<
BgBlock
className=
{
styles
.
banish
}
glowing=
{
!
op
&&
glowingBanish
}
/>
<
BgBlock
<
BgBlock
className=
{
styles
.
graveyard
}
glowing=
{
!
op
&&
glowingGraveyard
}
/>
className=
{
styles
.
banish
}
glowing=
{
!
op
&&
glowingBanish
}
chains=
{
{
chains
:
genChains
(
removed
),
banish
:
true
,
op
}
}
/>
<
BgBlock
className=
{
styles
.
graveyard
}
glowing=
{
!
op
&&
glowingGraveyard
}
chains=
{
{
chains
:
genChains
(
grave
),
graveyard
:
true
,
op
}
}
/>
<
BgBlock
<
BgBlock
className=
{
styles
.
field
}
className=
{
styles
.
field
}
onClick=
{
()
=>
onBlockClick
(
field
.
interactivity
)
}
onClick=
{
()
=>
onBlockClick
(
field
.
interactivity
)
}
disabled=
{
field
.
disabled
}
disabled=
{
field
.
disabled
}
highlight=
{
!!
field
.
interactivity
}
highlight=
{
!!
field
.
interactivity
}
chains=
{
{
chains
:
field
.
chainIndex
,
field
:
true
,
op
}
}
/>
/>
<
BgBlock
className=
{
styles
.
deck
}
/>
<
BgBlock
className=
{
styles
.
deck
}
chains=
{
{
chains
:
[]
}
}
/>
<
BgBlock
<
BgBlock
className=
{
classnames
(
styles
.
deck
,
styles
[
"
extra-deck
"
])
}
className=
{
classnames
(
styles
.
deck
,
styles
[
"
extra-deck
"
])
}
glowing=
{
!
op
&&
glowingExtra
}
glowing=
{
!
op
&&
glowingExtra
}
chains=
{
{
chains
:
genChains
(
extra
),
extra
:
true
,
op
}
}
/>
/>
</
div
>
</
div
>
);
);
...
@@ -114,14 +142,14 @@ export const Bg: React.FC = () => {
...
@@ -114,14 +142,14 @@ export const Bg: React.FC = () => {
const
snap
=
useSnapshot
(
placeStore
.
inner
);
const
snap
=
useSnapshot
(
placeStore
.
inner
);
return
(
return
(
<
div
className=
{
styles
[
"
mat-bg
"
]
}
>
<
div
className=
{
styles
[
"
mat-bg
"
]
}
>
<
BgRow
snap=
{
snap
[
ygopro
.
CardZone
.
SZONE
].
op
}
szone
opponent
/>
<
BgRow
snap=
{
snap
[
SZONE
].
op
}
szone
opponent
/>
<
BgRow
snap=
{
snap
[
ygopro
.
CardZone
.
MZONE
].
op
}
opponent
/>
<
BgRow
snap=
{
snap
[
MZONE
].
op
}
opponent
/>
<
BgExtraRow
<
BgExtraRow
meSnap=
{
snap
[
ygopro
.
CardZone
.
MZONE
].
me
.
slice
(
5
,
7
)
}
meSnap=
{
snap
[
MZONE
].
me
.
slice
(
5
,
7
)
}
opSnap=
{
snap
[
ygopro
.
CardZone
.
MZONE
].
op
.
slice
(
5
,
7
)
}
opSnap=
{
snap
[
MZONE
].
op
.
slice
(
5
,
7
)
}
/>
/>
<
BgRow
snap=
{
snap
[
ygopro
.
CardZone
.
MZONE
].
me
}
/>
<
BgRow
snap=
{
snap
[
MZONE
].
me
}
/>
<
BgRow
snap=
{
snap
[
ygopro
.
CardZone
.
SZONE
].
me
}
szone
/>
<
BgRow
snap=
{
snap
[
SZONE
].
me
}
szone
/>
<
BgOtherBlocks
/>
<
BgOtherBlocks
/>
<
BgOtherBlocks
op
/>
<
BgOtherBlocks
op
/>
</
div
>
</
div
>
...
...
src/ui/Duel/PlayMat/HandChain/index.module.scss
0 → 100644
View file @
b1fba04e
.container
{
position
:
fixed
;
display
:
flex
;
overflow
:
hidden
;
z-index
:
2
;
.me
{
position
:
fixed
;
bottom
:
1rem
;
right
:
50%
;
}
.op
{
position
:
fixed
;
top
:
1rem
;
left
:
50%
;
}
}
src/ui/Duel/PlayMat/HandChain/index.tsx
0 → 100644
View file @
b1fba04e
import
{
type
INTERNAL_Snapshot
as
Snapshot
,
useSnapshot
}
from
"
valtio
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
BlockState
,
placeStore
}
from
"
@/stores
"
;
import
{
BgChain
}
from
"
@/ui/Shared
"
;
import
styles
from
"
./index.module.scss
"
;
const
{
HAND
}
=
ygopro
.
CardZone
;
export
const
HandChain
:
React
.
FC
=
()
=>
{
const
snap
=
useSnapshot
(
placeStore
.
inner
);
const
me
=
snap
[
HAND
].
me
;
const
op
=
snap
[
HAND
].
op
;
const
genChains
=
(
states
:
Snapshot
<
BlockState
[]
>
)
=>
{
const
chains
:
number
[]
=
states
.
flatMap
((
state
)
=>
state
.
chainIndex
);
chains
.
sort
();
return
chains
;
};
return
(
<
div
className=
{
styles
.
container
}
>
<
div
className=
{
styles
.
me
}
>
<
BgChain
chains=
{
genChains
(
me
)
}
/>
</
div
>
<
div
className=
{
styles
.
op
}
>
<
BgChain
chains=
{
genChains
(
op
)
}
op
/>
</
div
>
</
div
>
);
};
src/ui/Shared/Chain/index.module.scss
0 → 100644
View file @
b1fba04e
.container
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
min-width
:
8rem
;
max-width
:
10rem
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
z-index
:
2
;
pointer-events
:
none
;
}
.banish
,
.graveyard
{
left
:
100%
;
}
.field
,
.extra-deck
{
right
:
100%
;
}
.op
{
transform
:
rotate
(
180deg
);
}
.chain
{
position
:
relative
;
width
:
50%
;
height
:
50%
;
display
:
flex
;
img
{
width
:
100%
;
height
:
100%
;
object-fit
:
contain
;
animation
:
rotate
5s
linear
infinite
;
}
.text
{
position
:
absolute
;
font-size
:
2rem
;
font-weight
:
bold
;
top
:
50%
;
left
:
50%
;
-webkit-text-stroke
:
1px
black
;
transform
:
translate
(
-50%
,
-50%
);
}
@keyframes
rotate
{
from
{
transform
:
rotate
(
0deg
);
}
to
{
transform
:
rotate
(
360deg
);
}
}
}
src/ui/Shared/Chain/index.tsx
0 → 100644
View file @
b1fba04e
import
classnames
from
"
classnames
"
;
import
{
useConfig
}
from
"
@/config
"
;
import
styles
from
"
./index.module.scss
"
;
const
{
assetsPath
}
=
useConfig
();
export
interface
ChainProps
{
chains
:
readonly
number
[];
banish
?:
boolean
;
graveyard
?:
boolean
;
extra
?:
boolean
;
field
?:
boolean
;
op
?:
boolean
;
}
/* 这里有个妥协的实现:墓地,除外区,额外卡组的连锁图标会被卡片遮挡,原因不明,
* 因此这里暂时采取移动一个身位的方式进行解决。最好的解决方案应该是UI上连锁图标和
* 场地解耦。 */
export
const
BgChain
:
React
.
FC
<
ChainProps
>
=
({
chains
,
banish
,
graveyard
,
extra
,
field
,
op
,
})
=>
(
<
div
className=
{
classnames
(
styles
.
container
,
{
[
styles
.
banish
]:
banish
,
[
styles
.
graveyard
]:
graveyard
,
[
styles
[
"
extra-deck
"
]]:
extra
,
[
styles
.
field
]:
field
,
[
styles
.
op
]:
op
,
})
}
>
{
chains
.
map
((
chain
)
=>
(
<
div
className=
{
styles
.
chain
}
key=
{
chain
}
>
<
img
src=
{
`${assetsPath}/chain.png`
}
/>
<
div
className=
{
styles
.
text
}
>
{
chain
}
</
div
>
</
div
>
))
}
</
div
>
);
src/ui/Shared/index.ts
View file @
b1fba04e
export
*
from
"
./Background
"
;
export
*
from
"
./Background
"
;
export
*
from
"
./CardEffectText
"
;
export
*
from
"
./CardEffectText
"
;
export
*
from
"
./Chain
"
;
export
*
from
"
./chatHook
"
;
export
*
from
"
./chatHook
"
;
export
*
from
"
./css
"
;
export
*
from
"
./css
"
;
export
*
from
"
./DeckCard
"
;
export
*
from
"
./DeckCard
"
;
...
...
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