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
f15b790f
Commit
f15b790f
authored
Jul 09, 2023
by
Chunchi Che
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev/dropmenu' into 'main'
optimize: card dropdown menu See merge request
!242
parents
2e712f62
9d4a6da6
Changes
29
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
438 additions
and
364 deletions
+438
-364
src/config/defaults.ts
src/config/defaults.ts
+1
-1
src/env.d.ts
src/env.d.ts
+1
-1
src/service/duel/move.ts
src/service/duel/move.ts
+8
-6
src/stores/matStore/types.ts
src/stores/matStore/types.ts
+1
-1
src/stores/placeStore.ts
src/stores/placeStore.ts
+10
-28
src/styles/core.scss
src/styles/core.scss
+5
-1
src/ui/Duel/Main.tsx
src/ui/Duel/Main.tsx
+1
-2
src/ui/Duel/Message/HintNotification/index.tsx
src/ui/Duel/Message/HintNotification/index.tsx
+3
-3
src/ui/Duel/Message/SelectCardsModal/index.tsx
src/ui/Duel/Message/SelectCardsModal/index.tsx
+1
-1
src/ui/Duel/PlayMat/Card/index.scss
src/ui/Duel/PlayMat/Card/index.scss
+6
-3
src/ui/Duel/PlayMat/Card/index.tsx
src/ui/Duel/PlayMat/Card/index.tsx
+174
-158
src/ui/Duel/PlayMat/Card/springs/attack.ts
src/ui/Duel/PlayMat/Card/springs/attack.ts
+1
-1
src/ui/Duel/PlayMat/Card/springs/focus.ts
src/ui/Duel/PlayMat/Card/springs/focus.ts
+8
-3
src/ui/Duel/PlayMat/Card/springs/index.ts
src/ui/Duel/PlayMat/Card/springs/index.ts
+1
-0
src/ui/Duel/PlayMat/Card/springs/moveToDeck.ts
src/ui/Duel/PlayMat/Card/springs/moveToDeck.ts
+5
-4
src/ui/Duel/PlayMat/Card/springs/moveToGround.ts
src/ui/Duel/PlayMat/Card/springs/moveToGround.ts
+37
-26
src/ui/Duel/PlayMat/Card/springs/moveToHand.ts
src/ui/Duel/PlayMat/Card/springs/moveToHand.ts
+6
-6
src/ui/Duel/PlayMat/Card/springs/moveToOutside.ts
src/ui/Duel/PlayMat/Card/springs/moveToOutside.ts
+8
-8
src/ui/Duel/PlayMat/Card/springs/moveToToken.ts
src/ui/Duel/PlayMat/Card/springs/moveToToken.ts
+8
-0
src/ui/Duel/PlayMat/Card/springs/types.ts
src/ui/Duel/PlayMat/Card/springs/types.ts
+2
-0
src/ui/Duel/PlayMat/Card/springs/utils.ts
src/ui/Duel/PlayMat/Card/springs/utils.ts
+12
-1
src/ui/Duel/PlayMat/LifeBar/index.scss
src/ui/Duel/PlayMat/LifeBar/index.scss
+49
-5
src/ui/Duel/PlayMat/LifeBar/index.tsx
src/ui/Duel/PlayMat/LifeBar/index.tsx
+89
-21
src/ui/Duel/PlayMat/Menu/index.scss
src/ui/Duel/PlayMat/Menu/index.scss
+0
-22
src/ui/Duel/PlayMat/Menu/index.tsx
src/ui/Duel/PlayMat/Menu/index.tsx
+0
-1
src/ui/Duel/PlayMat/Timer/index.scss
src/ui/Duel/PlayMat/Timer/index.scss
+0
-25
src/ui/Duel/PlayMat/Timer/index.tsx
src/ui/Duel/PlayMat/Timer/index.tsx
+0
-34
src/ui/Duel/PlayMat/index.ts
src/ui/Duel/PlayMat/index.ts
+0
-1
src/ui/Duel/PlayMat/utils/cssConfig.ts
src/ui/Duel/PlayMat/utils/cssConfig.ts
+1
-1
No files found.
src/config/defaults.ts
View file @
f15b790f
...
@@ -19,7 +19,7 @@ const defaultConfig: DefaultsConfig = {
...
@@ -19,7 +19,7 @@ const defaultConfig: DefaultsConfig = {
const
aiModeConfig
:
DefaultsConfig
=
{
const
aiModeConfig
:
DefaultsConfig
=
{
...
defaultConfig
,
...
defaultConfig
,
defaultDeck
:
VITE_AI_MODE_DEFAULT_DECK
||
"
Hero
"
,
defaultDeck
:
VITE_AI_MODE_DEFAULT_DECK
||
"
Hero
"
,
defaultPlayer
:
`AiKiller
${
Math
.
random
().
toString
(
36
).
slice
(
2
)}
}`
,
defaultPlayer
:
`AiKiller
-
${
Math
.
random
().
toString
(
36
).
slice
(
2
,
6
)}
}`
,
defaultPassword
:
"
AI
"
,
defaultPassword
:
"
AI
"
,
};
};
...
...
src/env.d.ts
View file @
f15b790f
...
@@ -22,6 +22,6 @@ declare global {
...
@@ -22,6 +22,6 @@ declare global {
color
:
(
color
:
(
color
:
string
,
color
:
string
,
backgroundColor
?:
string
backgroundColor
?:
string
)
=>
(...
args
:
any
[]
)
=>
void
;
)
=>
(...
args
:
Parameters
<
console
.
log
>
)
=>
void
;
}
}
}
}
src/service/duel/move.ts
View file @
f15b790f
...
@@ -157,14 +157,15 @@ export default async (move: MsgMove) => {
...
@@ -157,14 +157,15 @@ export default async (move: MsgMove) => {
target
.
location
=
to
;
target
.
location
=
to
;
// 维护完了之后,开始动画
// 维护完了之后,开始动画
await
eventbus
.
call
(
Task
.
Move
,
target
.
uuid
);
await
eventbus
.
call
(
Task
.
Move
,
target
.
uuid
,
from
.
zone
);
// 如果from或者to是手卡,那么需要刷新除了这张卡之外,这个玩家的所有手卡
// 如果from或者to是手卡,那么需要刷新除了这张卡之外,这个玩家的所有手卡
if
([
from
.
zone
,
to
.
zone
].
includes
(
HAND
))
{
if
([
from
.
zone
,
to
.
zone
].
includes
(
HAND
))
{
for
(
const
card
of
cardStore
.
at
(
HAND
,
target
.
location
.
controller
))
{
await
Promise
.
all
(
if
(
card
.
uuid
!==
target
.
uuid
)
{
cardStore
await
eventbus
.
call
(
Task
.
Move
,
card
.
uuid
);
.
at
(
HAND
,
target
.
location
.
controller
)
}
.
filter
((
c
)
=>
c
.
uuid
!==
target
.
uuid
)
}
.
map
(
async
(
c
)
=>
await
eventbus
.
call
(
Task
.
Move
,
c
.
uuid
))
);
}
}
// 超量素材位置跟随超量怪兽移动
// 超量素材位置跟随超量怪兽移动
...
@@ -177,6 +178,7 @@ export default async (move: MsgMove) => {
...
@@ -177,6 +178,7 @@ export default async (move: MsgMove) => {
overlay
.
location
.
zone
=
to
.
zone
;
overlay
.
location
.
zone
=
to
.
zone
;
overlay
.
location
.
controller
=
to
.
controller
;
overlay
.
location
.
controller
=
to
.
controller
;
overlay
.
location
.
sequence
=
to
.
sequence
;
overlay
.
location
.
sequence
=
to
.
sequence
;
overlay
.
location
.
position
=
to
.
position
;
await
eventbus
.
call
(
Task
.
Move
,
overlay
.
uuid
);
await
eventbus
.
call
(
Task
.
Move
,
overlay
.
uuid
);
}
}
...
...
src/stores/matStore/types.ts
View file @
f15b790f
...
@@ -93,7 +93,7 @@ export interface HintState {
...
@@ -93,7 +93,7 @@ export interface HintState {
}
}
export
interface
PhaseState
{
export
interface
PhaseState
{
currentPhase
:
ygopro
.
StocGameMessage
.
MsgNewPhase
.
PhaseType
;
// TODO 当前的阶段 应该改成enum
currentPhase
:
ygopro
.
StocGameMessage
.
MsgNewPhase
.
PhaseType
;
enableBp
:
boolean
;
// 允许进入战斗阶段
enableBp
:
boolean
;
// 允许进入战斗阶段
enableM2
:
boolean
;
// 允许进入M2阶段
enableM2
:
boolean
;
// 允许进入M2阶段
enableEp
:
boolean
;
// 允许回合结束
enableEp
:
boolean
;
// 允许回合结束
...
...
src/stores/placeStore.ts
View file @
f15b790f
...
@@ -20,39 +20,21 @@ export interface BlockState {
...
@@ -20,39 +20,21 @@ export interface BlockState {
disabled
:
boolean
;
// 是否被禁用
disabled
:
boolean
;
// 是否被禁用
}
}
const
genPLaces
=
(
n
:
number
):
BlockState
[]
=>
Array
.
from
({
length
:
n
}).
map
(()
=>
({
interactivity
:
undefined
,
disabled
:
false
,
}));
export
const
placeStore
=
proxy
({
export
const
placeStore
=
proxy
({
inner
:
{
inner
:
{
[
MZONE
]:
{
[
MZONE
]:
{
me
:
Array
.
from
({
length
:
7
}).
map
(
me
:
genPLaces
(
7
),
()
=>
op
:
genPLaces
(
7
),
({
interactivity
:
undefined
,
disabled
:
false
,
}
as
BlockState
)
),
op
:
Array
.
from
({
length
:
7
}).
map
(
()
=>
({
interactivity
:
undefined
,
disabled
:
false
,
}
as
BlockState
)
),
},
},
[
SZONE
]:
{
[
SZONE
]:
{
me
:
Array
.
from
({
length
:
6
}).
map
(
me
:
genPLaces
(
6
),
()
=>
op
:
genPLaces
(
6
),
({
interactivity
:
undefined
,
disabled
:
false
,
}
as
BlockState
)
),
op
:
Array
.
from
({
length
:
6
}).
map
(
()
=>
({
interactivity
:
undefined
,
disabled
:
false
,
}
as
BlockState
)
),
},
},
},
},
set
(
set
(
...
...
src/styles/core.scss
View file @
f15b790f
...
@@ -41,7 +41,11 @@ body {
...
@@ -41,7 +41,11 @@ body {
margin
:
0
;
margin
:
0
;
place-items
:
center
;
place-items
:
center
;
min-width
:
320px
;
min-width
:
320px
;
min-height
:
100vh
;
position
:
fixed
;
top
:
0
;
bottom
:
0
;
left
:
0
;
right
:
0
;
}
}
a
{
a
{
...
...
src/ui/Duel/Main.tsx
View file @
f15b790f
...
@@ -14,7 +14,7 @@ import {
...
@@ -14,7 +14,7 @@ import {
SortCardModal
,
SortCardModal
,
YesNoModal
,
YesNoModal
,
}
from
"
./Message
"
;
}
from
"
./Message
"
;
import
{
LifeBar
,
Mat
,
Menu
,
Timer
}
from
"
./PlayMat
"
;
import
{
LifeBar
,
Mat
,
Menu
}
from
"
./PlayMat
"
;
const
NeosDuel
=
()
=>
{
const
NeosDuel
=
()
=>
{
return
(
return
(
...
@@ -23,7 +23,6 @@ const NeosDuel = () => {
...
@@ -23,7 +23,6 @@ const NeosDuel = () => {
<
Alert
/>
<
Alert
/>
<
Menu
/>
<
Menu
/>
<
LifeBar
/>
<
LifeBar
/>
<
Timer
/>
<
Mat
/>
<
Mat
/>
<
CardModal
/>
<
CardModal
/>
<
CardListModal
/>
<
CardListModal
/>
...
...
src/ui/Duel/Message/HintNotification/index.tsx
View file @
f15b790f
...
@@ -10,9 +10,9 @@ import { useConfig } from "@/config";
...
@@ -10,9 +10,9 @@ import { useConfig } from "@/config";
import
{
HandResult
,
matStore
}
from
"
@/stores
"
;
import
{
HandResult
,
matStore
}
from
"
@/stores
"
;
const
style
=
{
const
style
=
{
borderStyle
:
"
groove
"
,
//
borderStyle: "groove",
borderRadius
:
"
8px
"
,
//
borderRadius: "8px",
backgroundColor
:
"
#
303030
"
,
backgroundColor
:
"
#
444
"
,
};
};
const
NeosConfig
=
useConfig
();
const
NeosConfig
=
useConfig
();
...
...
src/ui/Duel/Message/SelectCardsModal/index.tsx
View file @
f15b790f
...
@@ -30,7 +30,7 @@ const CheckCardStyle = {
...
@@ -30,7 +30,7 @@ const CheckCardStyle = {
};
};
const
CheckGroupStyle
=
{
const
CheckGroupStyle
=
{
display
:
"
grid
"
,
display
:
"
grid
"
,
gridTemplateColumns
:
"
repeat(
6
, 1fr)
"
,
gridTemplateColumns
:
"
repeat(
5
, 1fr)
"
,
gap
:
10
,
gap
:
10
,
};
};
...
...
src/ui/Duel/PlayMat/Card/index.scss
View file @
f15b790f
...
@@ -13,7 +13,7 @@ section#mat {
...
@@ -13,7 +13,7 @@ section#mat {
top
:
2%
;
top
:
2%
;
height
:
96%
;
height
:
96%
;
width
:
96%
;
width
:
96%
;
transform
:
translateZ
(
calc
(
var
(
--
z
)
*
1px
+
0
.1px
))
transform
:
translateZ
(
calc
(
(
var
(
--
z
)
+
var
(
--
sub-z
)
)
*
1px
+
0
.1px
))
rotateY
(
calc
(
var
(
--
ry
)
*
1deg
));
rotateY
(
calc
(
var
(
--
ry
)
*
1deg
));
transition
:
0
.2s
scale
;
transition
:
0
.2s
scale
;
cursor
:
pointer
;
cursor
:
pointer
;
...
@@ -107,9 +107,12 @@ section#mat {
...
@@ -107,9 +107,12 @@ section#mat {
}
}
.mat-card.highlight
.card-shadow
{
.mat-card.highlight
.card-shadow
{
--card-shadow-color
:
#0099ff
;
display
:
block
!
important
;
display
:
block
!
important
;
background
:
linear-gradient
(
to
right
,
#0079c6
,
#009cff
)
!
important
;
background
:
var
(
--
card-shadow-color
)
!
important
;
filter
:
blur
(
8px
);
border-radius
:
5px
;
box-shadow
:
0
0
4px
0
var
(
--
card-shadow-color
)
,
0
0
25px
2px
#0099ff
87
;
transform
:
translateZ
(
calc
((
var
(
--
z
))
*
1px
+
0
.1px
));
}
}
@keyframes
focus
{
@keyframes
focus
{
...
...
src/ui/Duel/PlayMat/Card/index.tsx
View file @
f15b790f
This diff is collapsed.
Click to expand it.
src/ui/Duel/PlayMat/Card/springs/attack.ts
View file @
f15b790f
...
@@ -5,7 +5,7 @@ import { ygopro } from "@/api";
...
@@ -5,7 +5,7 @@ import { ygopro } from "@/api";
import
{
CardType
,
isMe
}
from
"
@/stores
"
;
import
{
CardType
,
isMe
}
from
"
@/stores
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
SpringApi
}
from
"
./types
"
;
import
type
{
SpringApi
}
from
"
./types
"
;
import
{
asyncStart
}
from
"
./utils
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
COL_GAP
,
ROW_GAP
}
=
const
{
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
COL_GAP
,
ROW_GAP
}
=
...
...
src/ui/Duel/PlayMat/Card/springs/focus.ts
View file @
f15b790f
import
{
ygopro
}
from
"
@/api
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
type
CardType
,
matStore
}
from
"
@/stores
"
;
import
{
type
CardType
,
matStore
}
from
"
@/stores
"
;
import
{
SpringApi
}
from
"
./types
"
;
import
type
{
SpringApi
}
from
"
./types
"
;
import
{
asyncStart
}
from
"
./utils
"
;
import
{
asyncStart
}
from
"
./utils
"
;
/** 发动效果的动画 */
/** 发动效果的动画 */
...
@@ -13,11 +13,16 @@ export const focus = async (props: { card: CardType; api: SpringApi }) => {
...
@@ -13,11 +13,16 @@ export const focus = async (props: { card: CardType; api: SpringApi }) => {
)
{
)
{
const
current
=
api
.
current
[
0
].
get
();
const
current
=
api
.
current
[
0
].
get
();
await
asyncStart
(
api
)({
await
asyncStart
(
api
)({
y
:
current
.
y
+
(
matStore
.
isMe
(
card
.
location
.
controller
)
?
-
1
:
1
)
*
20
0
,
// TODO: 放到config之中
y
:
current
.
y
+
(
matStore
.
isMe
(
card
.
location
.
controller
)
?
-
1
:
1
)
*
12
0
,
// TODO: 放到config之中
ry
:
0
,
ry
:
0
,
rz
:
0
,
rz
:
0
,
});
});
await
asyncStart
(
api
)({
y
:
current
.
y
,
ry
:
current
.
ry
,
rz
:
current
.
rz
});
await
asyncStart
(
api
)({
y
:
current
.
y
,
ry
:
current
.
ry
,
rz
:
current
.
rz
,
z
:
current
.
z
,
});
}
else
{
}
else
{
await
asyncStart
(
api
)({
await
asyncStart
(
api
)({
focusScale
:
1.5
,
focusScale
:
1.5
,
...
...
src/ui/Duel/PlayMat/Card/springs/index.ts
View file @
f15b790f
...
@@ -4,3 +4,4 @@ export * from "./moveToDeck";
...
@@ -4,3 +4,4 @@ export * from "./moveToDeck";
export
*
from
"
./moveToGround
"
;
export
*
from
"
./moveToGround
"
;
export
*
from
"
./moveToHand
"
;
export
*
from
"
./moveToHand
"
;
export
*
from
"
./moveToOutside
"
;
export
*
from
"
./moveToOutside
"
;
export
*
from
"
./moveToToken
"
;
src/ui/Duel/PlayMat/Card/springs/moveToDeck.ts
View file @
f15b790f
import
{
ygopro
}
from
"
@/api
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
type
CardType
,
isMe
}
from
"
@/stores
"
;
import
{
isMe
}
from
"
@/stores
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
SpringApi
}
from
"
./type
s
"
;
import
{
asyncStart
,
type
MoveFunc
}
from
"
./util
s
"
;
const
{
const
{
BLOCK_WIDTH
,
BLOCK_WIDTH
,
...
@@ -18,7 +18,7 @@ const {
...
@@ -18,7 +18,7 @@ const {
const
{
DECK
,
EXTRA
}
=
ygopro
.
CardZone
;
const
{
DECK
,
EXTRA
}
=
ygopro
.
CardZone
;
export
const
moveToDeck
=
async
(
props
:
{
card
:
CardType
;
api
:
SpringApi
}
)
=>
{
export
const
moveToDeck
:
MoveFunc
=
async
(
props
)
=>
{
const
{
card
,
api
}
=
props
;
const
{
card
,
api
}
=
props
;
// report
// report
const
{
location
}
=
card
;
const
{
location
}
=
card
;
...
@@ -41,7 +41,8 @@ export const moveToDeck = async (props: { card: CardType; api: SpringApi }) => {
...
@@ -41,7 +41,8 @@ export const moveToDeck = async (props: { card: CardType; api: SpringApi }) => {
let
rz
=
zone
===
EXTRA
?
DECK_ROTATE_Z
.
value
:
-
DECK_ROTATE_Z
.
value
;
let
rz
=
zone
===
EXTRA
?
DECK_ROTATE_Z
.
value
:
-
DECK_ROTATE_Z
.
value
;
rz
+=
isMe
(
controller
)
?
0
:
180
;
rz
+=
isMe
(
controller
)
?
0
:
180
;
const
z
=
sequence
;
const
z
=
sequence
;
api
.
start
({
await
asyncStart
(
api
)({
x
,
x
,
y
,
y
,
z
,
z
,
...
...
src/ui/Duel/PlayMat/Card/springs/moveToGround.ts
View file @
f15b790f
import
{
easings
}
from
"
@react-spring/web
"
;
import
{
easings
}
from
"
@react-spring/web
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
type
CardType
,
isMe
}
from
"
@/stores
"
;
import
{
isMe
}
from
"
@/stores
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
SpringApi
}
from
"
./types
"
;
import
{
asyncStart
,
type
MoveFunc
}
from
"
./utils
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
const
{
BLOCK_WIDTH
,
BLOCK_WIDTH
,
...
@@ -16,13 +15,10 @@ const {
...
@@ -16,13 +15,10 @@ const {
ROW_GAP
,
ROW_GAP
,
}
=
matConfig
;
}
=
matConfig
;
const
{
MZONE
,
SZONE
}
=
ygopro
.
CardZone
;
const
{
MZONE
,
SZONE
,
TZONE
}
=
ygopro
.
CardZone
;
export
const
moveToGround
=
async
(
props
:
{
export
const
moveToGround
:
MoveFunc
=
async
(
props
)
=>
{
card
:
CardType
;
const
{
card
,
api
,
fromZone
}
=
props
;
api
:
SpringApi
;
})
=>
{
const
{
card
,
api
}
=
props
;
const
{
location
}
=
card
;
const
{
location
}
=
card
;
...
@@ -84,26 +80,41 @@ export const moveToGround = async (props: {
...
@@ -84,26 +80,41 @@ export const moveToGround = async (props: {
let
rz
=
isMe
(
controller
)
?
0
:
180
;
let
rz
=
isMe
(
controller
)
?
0
:
180
;
rz
+=
defence
?
90
:
0
;
rz
+=
defence
?
90
:
0
;
const
ry
=
[
ygopro
.
CardPosition
.
FACEDOWN
,
ygopro
.
CardPosition
.
FACEDOWN_ATTACK
,
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
,
].
includes
(
position
??
5
)
?
180
:
0
;
// 动画
// 动画
if
(
fromZone
===
TZONE
)
{
// 如果是Token,直接先移动到那个位置,然后再放大
api
.
set
({
x
,
y
,
ry
,
rz
,
height
:
0
,
});
}
else
{
await
asyncStart
(
api
)({
x
,
y
,
height
,
z
:
is_overlay
?
120
:
200
,
ry
,
rz
,
config
:
{
// mass: 0.5,
easing
:
easings
.
easeInOutSine
,
},
});
}
await
asyncStart
(
api
)({
await
asyncStart
(
api
)({
x
,
y
,
height
,
height
,
z
:
is_overlay
?
120
:
200
,
ry
:
[
ygopro
.
CardPosition
.
FACEDOWN
,
ygopro
.
CardPosition
.
FACEDOWN_ATTACK
,
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
,
].
includes
(
position
??
5
)
?
180
:
0
,
rz
,
config
:
{
// mass: 0.5,
easing
:
easings
.
easeInOutSine
,
},
});
await
asyncStart
(
api
)({
z
:
0
,
z
:
0
,
zIndex
:
is_overlay
?
1
:
3
,
zIndex
:
is_overlay
?
1
:
3
,
config
:
{
config
:
{
...
...
src/ui/Duel/PlayMat/Card/springs/moveToHand.ts
View file @
f15b790f
import
{
ygopro
}
from
"
@/api
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
cardStore
,
type
CardType
,
isMe
}
from
"
@/stores
"
;
import
{
cardStore
,
isMe
}
from
"
@/stores
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
SpringApi
}
from
"
./type
s
"
;
import
{
asyncStart
,
type
MoveFunc
}
from
"
./util
s
"
;
const
{
const
{
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_M
,
...
@@ -16,7 +16,7 @@ const {
...
@@ -16,7 +16,7 @@ const {
const
{
HAND
}
=
ygopro
.
CardZone
;
const
{
HAND
}
=
ygopro
.
CardZone
;
export
const
moveToHand
=
async
(
props
:
{
card
:
CardType
;
api
:
SpringApi
}
)
=>
{
export
const
moveToHand
:
MoveFunc
=
async
(
props
)
=>
{
const
{
card
,
api
}
=
props
;
const
{
card
,
api
}
=
props
;
const
{
sequence
,
controller
}
=
card
.
location
;
const
{
sequence
,
controller
}
=
card
.
location
;
// 手卡会有很复杂的计算...
// 手卡会有很复杂的计算...
...
@@ -44,14 +44,14 @@ export const moveToHand = async (props: { card: CardType; api: SpringApi }) => {
...
@@ -44,14 +44,14 @@ export const moveToHand = async (props: { card: CardType; api: SpringApi }) => {
const
negativeX
=
Math
.
sin
(
angle
)
*
r
;
const
negativeX
=
Math
.
sin
(
angle
)
*
r
;
const
negativeY
=
Math
.
cos
(
angle
)
*
r
+
HAND_CARD_HEIGHT
.
value
/
2
;
const
negativeY
=
Math
.
cos
(
angle
)
*
r
+
HAND_CARD_HEIGHT
.
value
/
2
;
const
x
=
hand_circle_center_x
+
negativeX
*
(
isMe
(
controller
)
?
1
:
-
1
);
const
x
=
hand_circle_center_x
+
negativeX
*
(
isMe
(
controller
)
?
1
:
-
1
);
const
y
=
hand_circle_center_y
-
negativeY
+
140
;
// 常量 是手动调的 这里肯定有问题 有空来修
const
y
=
hand_circle_center_y
-
negativeY
+
140
;
//
FIXME:
常量 是手动调的 这里肯定有问题 有空来修
const
_rz
=
(
angle
*
180
)
/
Math
.
PI
;
const
_rz
=
(
angle
*
180
)
/
Math
.
PI
;
a
pi
.
start
({
a
wait
asyncStart
(
api
)
({
x
:
isMe
(
controller
)
?
x
:
-
x
,
x
:
isMe
(
controller
)
?
x
:
-
x
,
y
:
isMe
(
controller
)
?
y
:
-
y
,
y
:
isMe
(
controller
)
?
y
:
-
y
,
z
:
0
,
z
:
sequence
+
5
,
rz
:
isMe
(
controller
)
?
_rz
:
180
-
_rz
,
rz
:
isMe
(
controller
)
?
_rz
:
180
-
_rz
,
ry
:
isMe
(
controller
)
?
0
:
180
,
ry
:
isMe
(
controller
)
?
0
:
180
,
height
:
HAND_CARD_HEIGHT
.
value
,
height
:
HAND_CARD_HEIGHT
.
value
,
...
...
src/ui/Duel/PlayMat/Card/springs/moveToOutside.ts
View file @
f15b790f
import
{
ygopro
}
from
"
@/api
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
type
CardType
,
isMe
}
from
"
@/stores
"
;
import
{
isMe
}
from
"
@/stores
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
{
SpringApi
}
from
"
./type
s
"
;
import
{
asyncStart
,
type
MoveFunc
}
from
"
./util
s
"
;
const
{
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
COL_GAP
,
ROW_GAP
}
=
const
{
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
COL_GAP
,
ROW_GAP
}
=
matConfig
;
matConfig
;
const
{
GRAVE
}
=
ygopro
.
CardZone
;
const
{
GRAVE
}
=
ygopro
.
CardZone
;
export
const
moveToOutside
=
async
(
props
:
{
export
const
moveToOutside
:
MoveFunc
=
async
(
props
)
=>
{
card
:
CardType
;
api
:
SpringApi
;
})
=>
{
const
{
card
,
api
}
=
props
;
const
{
card
,
api
}
=
props
;
// report
// report
const
{
zone
,
controller
,
position
}
=
card
.
location
;
const
{
zone
,
controller
,
position
,
sequence
}
=
card
.
location
;
let
x
=
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
)
*
3
,
let
x
=
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
)
*
3
,
y
=
zone
===
GRAVE
?
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
:
0
;
y
=
zone
===
GRAVE
?
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
:
0
;
...
@@ -23,12 +20,15 @@ export const moveToOutside = async (props: {
...
@@ -23,12 +20,15 @@ export const moveToOutside = async (props: {
x
=
-
x
;
x
=
-
x
;
y
=
-
y
;
y
=
-
y
;
}
}
a
pi
.
start
({
a
wait
asyncStart
(
api
)
({
x
,
x
,
y
,
y
,
z
:
0
,
z
:
0
,
height
:
BLOCK_HEIGHT_S
.
value
,
height
:
BLOCK_HEIGHT_S
.
value
,
rz
:
isMe
(
controller
)
?
0
:
180
,
rz
:
isMe
(
controller
)
?
0
:
180
,
ry
:
[
ygopro
.
CardPosition
.
FACEDOWN
].
includes
(
position
)
?
180
:
0
,
ry
:
[
ygopro
.
CardPosition
.
FACEDOWN
].
includes
(
position
)
?
180
:
0
,
subZ
:
100
,
zIndex
:
sequence
,
});
});
api
.
set
({
subZ
:
0
});
};
};
src/ui/Duel/PlayMat/Card/springs/moveToToken.ts
0 → 100644
View file @
f15b790f
import
{
asyncStart
,
type
MoveFunc
}
from
"
./utils
"
;
export
const
moveToToken
:
MoveFunc
=
async
(
props
)
=>
{
const
{
api
}
=
props
;
await
asyncStart
(
api
)({
height
:
0
,
});
};
src/ui/Duel/PlayMat/Card/springs/types.ts
View file @
f15b790f
...
@@ -14,6 +14,8 @@ export interface SpringApiProps {
...
@@ -14,6 +14,8 @@ export interface SpringApiProps {
focusDisplay
:
string
;
focusDisplay
:
string
;
focusOpacity
:
number
;
focusOpacity
:
number
;
// <<< focus
// <<< focus
subZ
:
number
;
// 0 -> 100,这是为了让卡片移动过程中,稍微上浮一些,避免一些奇怪的遮挡问题
}
}
export
type
SpringApi
=
SpringRef
<
SpringApiProps
>
;
export
type
SpringApi
=
SpringRef
<
SpringApiProps
>
;
src/ui/Duel/PlayMat/Card/springs/utils.ts
View file @
f15b790f
import
{
type
SpringConfig
,
type
SpringRef
}
from
"
@react-spring/web
"
;
import
{
type
SpringConfig
,
type
SpringRef
}
from
"
@react-spring/web
"
;
import
type
{
ygopro
}
from
"
@/api
"
;
import
{
type
CardType
}
from
"
@/stores
"
;
import
type
{
SpringApi
}
from
"
./types
"
;
export
const
asyncStart
=
<
T
extends
{}
>
(
api
:
SpringRef
<
T
>
)
=>
{
export
const
asyncStart
=
<
T
extends
{}
>
(
api
:
SpringRef
<
T
>
)
=>
{
return
(
p
:
Partial
<
T
>
&
{
config
?:
SpringConfig
})
=>
return
(
p
:
Partial
<
T
>
&
{
config
?:
SpringConfig
})
=>
new
Promise
((
resolve
)
=>
{
new
Promise
((
resolve
)
=>
{
api
.
start
({
api
.
start
({
...
p
,
...
p
,
onRes
t
:
resolve
,
onRes
olve
:
resolve
,
});
});
});
});
};
};
export
type
MoveFunc
=
(
props
:
{
card
:
CardType
;
api
:
SpringApi
;
fromZone
?:
ygopro
.
CardZone
;
})
=>
Promise
<
void
>
;
src/ui/Duel/PlayMat/LifeBar/index.scss
View file @
f15b790f
...
@@ -3,20 +3,24 @@
...
@@ -3,20 +3,24 @@
display
:
flex
;
display
:
flex
;
top
:
0
;
top
:
0
;
left
:
0
;
left
:
0
;
height
:
100vh
;
// FIXME: 100% on safari
bottom
:
0
;
flex-direction
:
column
;
flex-direction
:
column
;
justify-content
:
space-between
;
justify-content
:
space-between
;
padding
:
20px
35px
;
padding
:
20px
;
margin-left
:
10px
;
pointer-events
:
none
;
pointer-events
:
none
;
z-index
:
100
;
z-index
:
100
;
--bg-color
:
#323232
;
width
:
200px
;
}
}
.life-bar
{
.life-bar
{
width
:
160px
;
position
:
relative
;
overflow
:
hidden
;
width
:
100%
;
color
:
white
;
color
:
white
;
background-color
:
#323232
;
background-color
:
var
(
--
bg-color
)
;
font-family
:
var
(
--
theme-font
);
font-family
:
var
(
--
theme-font
);
border
:
1px
solid
#222
;
padding
:
1rem
;
padding
:
1rem
;
padding-bottom
:
0
.6rem
;
padding-bottom
:
0
.6rem
;
border-radius
:
8px
;
border-radius
:
8px
;
...
@@ -32,3 +36,43 @@
...
@@ -32,3 +36,43 @@
font-size
:
1
.8rem
;
font-size
:
1
.8rem
;
}
}
}
}
.timer-container
{
background-color
:
var
(
--
bg-color
);
border-radius
:
4px
;
padding
:
0
.4rem
1rem
;
font-size
:
0
.8rem
;
font-weight
:
bold
;
font-family
:
var
(
--
theme-font
);
width
:
fit-content
;
color
:
white
;
display
:
flex
;
gap
:
8px
;
align-items
:
center
;
overflow
:
hidden
;
position
:
relative
;
.timer-text
{
min-width
:
3
.25em
;
}
}
.floodlight
{
position
:
absolute
;
height
:
100%
;
width
:
40px
;
background-color
:
#aaa
;
top
:
0
;
right
:
0
;
filter
:
blur
(
30px
);
transform
:
skewX
(
-20deg
);
}
.floodlight-run
{
animation
:
floodlight
4s
linear
infinite
;
}
@keyframes
floodlight
{
0
%
{
right
:
-80px
;
}
100
%
{
right
:
calc
(
100%
+
80px
);
}
}
src/ui/Duel/PlayMat/LifeBar/index.tsx
View file @
f15b790f
import
"
./index.scss
"
;
import
"
./index.scss
"
;
import
{
Progress
}
from
"
antd
"
;
import
classNames
from
"
classnames
"
;
import
classNames
from
"
classnames
"
;
import
React
,
{
useEffect
}
from
"
react
"
;
import
React
,
{
useEffect
,
useState
}
from
"
react
"
;
import
AnimatedNumbers
from
"
react-animated-numbers
"
;
import
AnimatedNumbers
from
"
react-animated-numbers
"
;
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
useEnv
}
from
"
@/hook
"
;
import
{
matStore
,
playerStore
}
from
"
@/stores
"
;
import
{
matStore
,
playerStore
}
from
"
@/stores
"
;
// 三个候选方案
// 三个候选方案
// https://snack.expo.dev/?platform=web
// https://snack.expo.dev/?platform=web
// https://github.com/heyman333/react-animated-numbers
// https://github.com/heyman333/react-animated-numbers
// https://www.npmjs.com/package/react-countup?activeTab=dependents
// https://www.npmjs.com/package/react-countup?activeTab=dependents
export
const
LifeBar
:
React
.
FC
=
()
=>
{
export
const
LifeBar
:
React
.
FC
=
()
=>
{
const
snap
=
useSnapshot
(
matStore
.
initInfo
);
const
snap
InitInfo
=
useSnapshot
(
matStore
.
initInfo
);
const
snapPlayer
=
useSnapshot
(
playerStore
);
const
snapPlayer
=
useSnapshot
(
playerStore
);
const
{
currentPlayer
}
=
useSnapshot
(
matStore
);
const
{
currentPlayer
}
=
useSnapshot
(
matStore
);
...
@@ -21,35 +22,102 @@ export const LifeBar: React.FC = () => {
...
@@ -21,35 +22,102 @@ export const LifeBar: React.FC = () => {
const
[
opLife
,
setOpLife
]
=
React
.
useState
(
0
);
const
[
opLife
,
setOpLife
]
=
React
.
useState
(
0
);
useEffect
(()
=>
{
useEffect
(()
=>
{
setMeLife
(
snap
.
me
.
life
);
setMeLife
(
snapInitInfo
.
me
.
life
);
},
[
snap
.
me
.
life
]);
},
[
snapInitInfo
.
me
.
life
]);
useEffect
(()
=>
{
setOpLife
(
snapInitInfo
.
op
.
life
);
},
[
snapInitInfo
.
op
.
life
]);
const
snapTimeLimit
=
useSnapshot
(
matStore
.
timeLimits
);
const
[
myTimeLimit
,
setMyTimeLimit
]
=
useState
(
snapTimeLimit
.
me
);
const
[
opTimeLimit
,
setOpTimeLimit
]
=
useState
(
snapTimeLimit
.
op
);
useEffect
(()
=>
{
setMyTimeLimit
(
snapTimeLimit
.
me
);
},
[
snapTimeLimit
.
me
]);
useEffect
(()
=>
{
setOpTimeLimit
(
snapTimeLimit
.
op
);
},
[
snapTimeLimit
.
op
]);
useEffect
(()
=>
{
setInterval
(()
=>
{
setMyTimeLimit
((
time
)
=>
time
-
1
);
setOpTimeLimit
((
time
)
=>
time
-
1
);
},
1000
);
},
[]);
useEffect
(()
=>
{
useEffect
(()
=>
{
setOpLife
(
snap
.
op
.
life
);
if
(
useEnv
().
VITE_IS_AI_MODE
)
{
},
[
snap
.
op
.
life
]);
// 如果是AI模式
// FIXME: 探索一个优雅的、判断当前是不是AI模式的方法,用户手动输入AI也是AI模式
setMyTimeLimit
(
240
);
setOpTimeLimit
(
240
);
}
},
[
currentPlayer
]);
return
(
return
(
<
div
id=
"life-bar-container"
>
<
div
id=
"life-bar-container"
>
<
LifeBarItem
active=
{
!
matStore
.
isMe
(
currentPlayer
)
}
name=
{
snapPlayer
.
getOpPlayer
().
name
??
"
?
"
}
life=
{
opLife
}
timeLimit=
{
opTimeLimit
}
isMe=
{
false
}
/>
<
LifeBarItem
active=
{
matStore
.
isMe
(
currentPlayer
)
}
name=
{
snapPlayer
.
getMePlayer
().
name
??
"
?
"
}
life=
{
meLife
}
timeLimit=
{
myTimeLimit
}
isMe=
{
true
}
/>
</
div
>
);
};
const
LifeBarItem
:
React
.
FC
<
{
active
:
boolean
;
name
:
string
;
life
:
number
;
timeLimit
:
number
;
isMe
:
boolean
;
}
>
=
({
active
,
name
,
life
,
timeLimit
,
isMe
})
=>
{
const
mm
=
Math
.
floor
(
timeLimit
/
60
);
const
ss
=
timeLimit
%
60
;
const
timeText
=
timeLimit
<
0
?
"
00:00
"
:
`
${
mm
<
10
?
"
0
"
+
mm
:
mm
}
:
${
ss
<
10
?
"
0
"
+
ss
:
ss
}
`
;
return
(
<
div
style=
{
{
flexDirection
:
isMe
?
"
column-reverse
"
:
"
column
"
,
overflow
:
"
hidden
"
,
display
:
"
flex
"
,
gap
:
"
0.5rem
"
,
position
:
"
relative
"
,
}
}
>
<
div
<
div
className=
{
classNames
(
"
life-bar
"
,
{
className=
{
classNames
(
"
life-bar
"
,
{
"
life-bar-activated
"
:
matStore
.
isMe
(
currentPlayer
)
,
"
life-bar-activated
"
:
active
,
})
}
})
}
>
>
<
div
className=
"name"
>
{
snapPlayer
.
getOpPlayer
().
name
}
</
div
>
<
div
className=
"name"
>
{
name
}
</
div
>
<
div
className=
"life"
>
<
div
className=
"life"
>
{
<
AnimatedNumbers
animateToNumber=
{
life
}
/>
}
</
div
>
{
<
AnimatedNumbers
animateToNumber=
{
opLife
}
/>
}
</
div
>
</
div
>
</
div
>
<
div
{
active
&&
(
className=
{
classNames
(
"
life-bar
"
,
{
<
div
className=
"timer-container"
>
"
life-bar-activated
"
:
matStore
.
isMe
(
currentPlayer
),
<
Progress
})
}
type=
"circle"
>
percent=
{
Math
.
floor
((
timeLimit
/
240
)
*
100
)
}
<
div
className=
"name"
>
{
snapPlayer
.
getMePlayer
().
name
}
</
div
>
strokeWidth=
{
20
}
<
div
className=
"life"
>
size=
{
14
}
<
AnimatedNumbers
animateToNumber=
{
meLife
}
/>
/>
<
div
className=
"timer-text"
>
{
timeText
}
</
div
>
<
div
className=
"floodlight floodlight-run"
/>
</
div
>
</
div
>
</
div
>
)
}
</
div
>
</
div
>
);
);
};
};
src/ui/Duel/PlayMat/Menu/index.scss
View file @
f15b790f
...
@@ -10,26 +10,4 @@
...
@@ -10,26 +10,4 @@
padding
:
8px
;
padding
:
8px
;
border-radius
:
6px
;
border-radius
:
6px
;
overflow
:
hidden
;
overflow
:
hidden
;
.floodlight
{
position
:
absolute
;
height
:
100%
;
width
:
40px
;
background-color
:
white
;
top
:
0
;
right
:
0
;
filter
:
blur
(
30px
);
transform
:
skewX
(
-20deg
);
}
.floodlight-run
{
animation
:
floodlight
1s
linear
infinite
;
}
}
@keyframes
floodlight
{
0
%
{
right
:
-80px
;
}
100
%
{
right
:
calc
(
100%
+
80px
);
}
}
}
src/ui/Duel/PlayMat/Menu/index.tsx
View file @
f15b790f
...
@@ -124,7 +124,6 @@ export const Menu = () => {
...
@@ -124,7 +124,6 @@ export const Menu = () => {
>
>
<
Button
icon=
{
<
CloseCircleFilled
/>
}
type=
"text"
></
Button
>
<
Button
icon=
{
<
CloseCircleFilled
/>
}
type=
"text"
></
Button
>
</
DropdownWithTitle
>
</
DropdownWithTitle
>
{
/* <div className="floodlight floodlight-run" /> */
}
</
div
>
</
div
>
</>
</>
);
);
...
...
src/ui/Duel/PlayMat/Timer/index.scss
deleted
100644 → 0
View file @
2e712f62
#timer-container
{
position
:
fixed
;
display
:
flex
;
top
:
0
;
right
:
0
;
height
:
100vh
;
padding
:
20px
35px
;
flex-direction
:
column
;
pointer-events
:
none
;
}
.timer
{
width
:
100px
;
color
:
white
;
background-color
:
#323232
;
font-family
:
var
(
--
theme-font
);
border
:
1px
solid
#222
;
padding
:
1rem
;
padding-bottom
:
0
.6rem
;
border-radius
:
8px
;
text-align
:
center
;
display
:
flex
;
flex-direction
:
column
;
font-size
:
1
.2rem
;
}
src/ui/Duel/PlayMat/Timer/index.tsx
deleted
100644 → 0
View file @
2e712f62
import
"
./index.scss
"
;
import
React
,
{
useEffect
,
useState
}
from
"
react
"
;
import
{
useSnapshot
}
from
"
valtio
"
;
import
{
matStore
}
from
"
@/stores
"
;
export
const
Timer
:
React
.
FC
=
()
=>
{
const
[
time
,
setTime
]
=
useState
(
0
);
const
snap
=
useSnapshot
(
matStore
);
useEffect
(()
=>
{
const
interval
=
setInterval
(()
=>
{
if
(
time
>
0
)
{
setTime
((
time
)
=>
time
-
1
);
}
},
1000
);
return
()
=>
clearInterval
(
interval
);
},
[
time
]);
useEffect
(()
=>
{
setTime
(
snap
.
timeLimits
.
me
);
},
[
snap
.
timeLimits
.
me
]);
useEffect
(()
=>
{
setTime
(
snap
.
timeLimits
.
op
);
},
[
snap
.
timeLimits
.
op
]);
return
(
<
div
id=
"timer-container"
>
<
div
className=
"timer"
>
{
time
}
</
div
>
</
div
>
);
};
src/ui/Duel/PlayMat/index.ts
View file @
f15b790f
export
*
from
"
./LifeBar
"
;
export
*
from
"
./LifeBar
"
;
export
*
from
"
./Mat
"
;
export
*
from
"
./Mat
"
;
export
*
from
"
./Menu
"
;
export
*
from
"
./Menu
"
;
export
*
from
"
./Timer
"
;
src/ui/Duel/PlayMat/utils/cssConfig.ts
View file @
f15b790f
...
@@ -65,7 +65,7 @@ export const matConfig = {
...
@@ -65,7 +65,7 @@ export const matConfig = {
unit
:
UNIT
.
PX
,
unit
:
UNIT
.
PX
,
},
},
HAND_CARD_HEIGHT
:
{
HAND_CARD_HEIGHT
:
{
value
:
1
4
0
,
value
:
1
3
0
,
unit
:
UNIT
.
PX
,
unit
:
UNIT
.
PX
,
},
},
DECK_OFFSET_X
:
{
DECK_OFFSET_X
:
{
...
...
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