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
a754c2c2
Commit
a754c2c2
authored
Jul 16, 2023
by
timel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev/ui' into 'main'
Dev/ui See merge request
mycard/Neos!246
parents
53a9bdf4
0cad7ff4
Changes
35
Hide whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
505 additions
and
456 deletions
+505
-456
src/infra/eventbus.ts
src/infra/eventbus.ts
+7
-4
src/service/duel/attack.ts
src/service/duel/attack.ts
+8
-10
src/service/duel/chaining.ts
src/service/duel/chaining.ts
+1
-5
src/service/duel/confirmCards.ts
src/service/duel/confirmCards.ts
+1
-4
src/service/duel/draw.ts
src/service/duel/draw.ts
+2
-2
src/service/duel/gameMsg.ts
src/service/duel/gameMsg.ts
+0
-6
src/service/duel/move.ts
src/service/duel/move.ts
+11
-10
src/service/duel/posChange.ts
src/service/duel/posChange.ts
+3
-2
src/service/duel/shuffleHandExtra.ts
src/service/duel/shuffleHandExtra.ts
+21
-19
src/service/duel/shuffleSetCard.ts
src/service/duel/shuffleSetCard.ts
+31
-28
src/service/duel/swapGraveDeck.ts
src/service/duel/swapGraveDeck.ts
+3
-3
src/service/duel/updateData.ts
src/service/duel/updateData.ts
+3
-4
src/ui/Duel/Message/SelectActionsModal/index.tsx
src/ui/Duel/Message/SelectActionsModal/index.tsx
+3
-26
src/ui/Duel/PlayMat/Bg/index.scss
src/ui/Duel/PlayMat/Bg/index.scss
+62
-3
src/ui/Duel/PlayMat/Bg/index.tsx
src/ui/Duel/PlayMat/Bg/index.tsx
+67
-25
src/ui/Duel/PlayMat/Card/index.scss
src/ui/Duel/PlayMat/Card/index.scss
+5
-1
src/ui/Duel/PlayMat/Card/index.tsx
src/ui/Duel/PlayMat/Card/index.tsx
+65
-77
src/ui/Duel/PlayMat/Card/springs/attack.ts
src/ui/Duel/PlayMat/Card/springs/attack.ts
+13
-19
src/ui/Duel/PlayMat/Card/springs/focus.ts
src/ui/Duel/PlayMat/Card/springs/focus.ts
+3
-8
src/ui/Duel/PlayMat/Card/springs/index.ts
src/ui/Duel/PlayMat/Card/springs/index.ts
+3
-5
src/ui/Duel/PlayMat/Card/springs/move.ts
src/ui/Duel/PlayMat/Card/springs/move.ts
+35
-0
src/ui/Duel/PlayMat/Card/springs/moveToDeck.ts
src/ui/Duel/PlayMat/Card/springs/moveToDeck.ts
+7
-12
src/ui/Duel/PlayMat/Card/springs/moveToGround.ts
src/ui/Duel/PlayMat/Card/springs/moveToGround.ts
+29
-29
src/ui/Duel/PlayMat/Card/springs/moveToHand.ts
src/ui/Duel/PlayMat/Card/springs/moveToHand.ts
+13
-16
src/ui/Duel/PlayMat/Card/springs/moveToOutside.ts
src/ui/Duel/PlayMat/Card/springs/moveToOutside.ts
+24
-9
src/ui/Duel/PlayMat/Card/springs/moveToToken.ts
src/ui/Duel/PlayMat/Card/springs/moveToToken.ts
+4
-1
src/ui/Duel/PlayMat/Card/springs/types.ts
src/ui/Duel/PlayMat/Card/springs/types.ts
+23
-1
src/ui/Duel/PlayMat/Card/springs/utils.ts
src/ui/Duel/PlayMat/Card/springs/utils.ts
+0
-11
src/ui/Duel/PlayMat/LifeBar/index.scss
src/ui/Duel/PlayMat/LifeBar/index.scss
+0
-22
src/ui/Duel/PlayMat/LifeBar/index.tsx
src/ui/Duel/PlayMat/LifeBar/index.tsx
+0
-1
src/ui/Duel/PlayMat/utils/cssConfig.ts
src/ui/Duel/PlayMat/utils/cssConfig.ts
+0
-91
src/ui/Duel/PlayMat/utils/index.ts
src/ui/Duel/PlayMat/utils/index.ts
+0
-1
src/ui/Shared/YgoCard/index.tsx
src/ui/Shared/YgoCard/index.tsx
+1
-1
src/ui/Shared/css.ts
src/ui/Shared/css.ts
+56
-0
src/ui/Shared/index.ts
src/ui/Shared/index.ts
+1
-0
No files found.
src/infra/eventbus.ts
View file @
a754c2c2
...
...
@@ -12,12 +12,15 @@ export enum Task {
const
getEnd
=
(
task
:
Task
)
=>
`
${
task
}
-end`
;
/** 在组件之中注册方法 */
const
register
=
(
task
:
Task
,
fn
:
(...
args
:
any
[])
=>
Promise
<
any
>
)
=>
{
const
register
=
<
T
extends
unknown
[]
>
(
task
:
Task
,
fn
:
(...
args
:
T
)
=>
Promise
<
boolean
>
)
=>
{
eventEmitter
.
on
(
task
,
async
({
taskId
,
args
}:
{
taskId
:
string
;
args
:
any
[]
})
=>
{
await
fn
(...
args
);
eventEmitter
.
emit
(
getEnd
(
task
),
taskId
);
async
({
taskId
,
args
}:
{
taskId
:
string
;
args
:
T
})
=>
{
const
result
=
await
fn
(...
args
);
if
(
result
)
eventEmitter
.
emit
(
getEnd
(
task
),
taskId
);
}
);
};
...
...
src/service/duel/attack.ts
View file @
a754c2c2
import
{
ygopro
}
from
"
@/api
"
;
import
{
eventbus
,
sleep
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
,
fetchEsHintMeta
}
from
"
@/stores
"
;
import
{
callCardAttack
}
from
"
@/ui/Duel/PlayMat/Card
"
;
export
default
async
(
attack
:
ygopro
.
StocGameMessage
.
MsgAttack
)
=>
{
fetchEsHintMeta
({
...
...
@@ -16,18 +16,16 @@ export default async (attack: ygopro.StocGameMessage.MsgAttack) => {
if
(
attacker
)
{
if
(
attack
.
direct_attack
)
{
await
eventbus
.
call
(
Task
.
Attack
,
attacker
.
uuid
,
true
);
await
callCardAttack
(
attacker
.
uuid
,
{
directAttack
:
true
,
});
}
else
{
await
eventbus
.
call
(
Task
.
Attack
,
attacker
.
uuid
,
false
,
attack
.
target_location
);
await
callCardAttack
(
attacker
.
uuid
,
{
directAttack
:
false
,
target
:
attack
.
target_location
,
});
}
}
else
{
console
.
warn
(
`<Attack>attacker from
${
attack
.
attacker_location
}
is null`
);
}
await
sleep
(
2000
);
};
src/service/duel/chaining.ts
View file @
a754c2c2
import
{
fetchCard
,
ygopro
}
from
"
@/api
"
;
import
{
eventbus
,
sleep
,
Task
}
from
"
@/infra
"
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
,
fetchEsHintMeta
,
matStore
}
from
"
@/stores
"
;
export
default
async
(
chaining
:
ygopro
.
StocGameMessage
.
MsgChaining
)
=>
{
...
...
@@ -29,10 +29,6 @@ export default async (chaining: ygopro.StocGameMessage.MsgChaining) => {
// 发动效果动画
await
eventbus
.
call
(
Task
.
Focus
,
target
.
uuid
);
console
.
color
(
"
blue
"
)(
`
${
target
.
meta
.
text
.
name
}
chaining`
);
// 临时办法,这里延迟800ms
// 长期:需要实现动画序列,一个动画完成后才执行下一个动画
await
sleep
(
800
);
}
else
{
console
.
warn
(
`<Chaining>target from
${
location
}
is null`
);
}
...
...
src/service/duel/confirmCards.ts
View file @
a754c2c2
import
{
fetchCard
,
ygopro
}
from
"
@/api
"
;
import
{
eventbus
,
sleep
,
Task
}
from
"
@/infra
"
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
}
from
"
@/stores
"
;
export
default
async
(
confirmCards
:
ygopro
.
StocGameMessage
.
MsgConfirmCards
)
=>
{
...
...
@@ -15,9 +15,6 @@ export default async (confirmCards: ygopro.StocGameMessage.MsgConfirmCards) => {
target
.
meta
=
meta
;
// 动画
await
eventbus
.
call
(
Task
.
Focus
,
target
.
uuid
);
// 临时措施,延迟一会,让动画逐个展示
// 长期:需要实现动画序列,一个动画完成后才执行下一个动画
await
sleep
(
500
);
}
else
{
console
.
warn
(
`card of
${
card
}
is null`
);
}
...
...
src/service/duel/draw.ts
View file @
a754c2c2
import
{
fetchCard
,
ygopro
}
from
"
@/api
"
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
,
fetchEsHintMeta
}
from
"
@/stores
"
;
import
{
callCardMove
}
from
"
@/ui/Duel/PlayMat/Card
"
;
export
default
async
(
draw
:
ygopro
.
StocGameMessage
.
MsgDraw
)
=>
{
fetchEsHintMeta
({
originMsg
:
"
玩家抽卡时
"
});
...
...
@@ -27,6 +27,6 @@ export default async (draw: ygopro.StocGameMessage.MsgDraw) => {
await
Promise
.
all
(
cardStore
.
at
(
ygopro
.
CardZone
.
HAND
,
draw
.
player
)
.
map
((
card
)
=>
eventbus
.
call
(
Task
.
Move
,
card
.
uuid
))
.
map
((
card
)
=>
callCardMove
(
card
.
uuid
))
);
};
src/service/duel/gameMsg.ts
View file @
a754c2c2
import
{
ygopro
}
from
"
@/api
"
;
import
{
sleep
}
from
"
@/infra
"
;
import
{
matStore
}
from
"
@/stores
"
;
import
{
showWaiting
}
from
"
@/ui/Duel/Message
"
;
...
...
@@ -93,7 +92,6 @@ let animation: Promise<unknown> = new Promise<void>((rs) => rs());
export
default
async
function
handleGameMsg
(
pb
:
ygopro
.
YgoStocMsg
)
{
animation
=
animation
.
then
(()
=>
_handleGameMsg
(
pb
));
// _handleGameMsg(pb);
}
async
function
_handleGameMsg
(
pb
:
ygopro
.
YgoStocMsg
)
{
...
...
@@ -143,8 +141,6 @@ async function _handleGameMsg(pb: ygopro.YgoStocMsg) {
}
case
"
move
"
:
{
await
onMsgMove
(
msg
.
move
);
await
sleep
(
500
);
break
;
}
case
"
select_card
"
:
{
...
...
@@ -269,7 +265,6 @@ async function _handleGameMsg(pb: ygopro.YgoStocMsg) {
}
case
"
chaining
"
:
{
await
onMsgChaining
(
msg
.
chaining
);
break
;
}
case
"
chain_solved
"
:
{
...
...
@@ -324,7 +319,6 @@ async function _handleGameMsg(pb: ygopro.YgoStocMsg) {
}
case
"
confirm_cards
"
:
{
await
onConfirmCards
(
msg
.
confirm_cards
);
break
;
}
case
"
become_target
"
:
{
...
...
src/service/duel/move.ts
View file @
a754c2c2
import
{
fetchCard
,
ygopro
}
from
"
@/api
"
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
,
CardType
}
from
"
@/stores
"
;
import
{
callCardMove
}
from
"
@/ui/Duel/PlayMat/Card
"
;
import
{
REASON_MATERIAL
,
TYPE_TOKEN
}
from
"
../../common
"
;
...
...
@@ -114,7 +114,7 @@ export default async (move: MsgMove) => {
overlayMaterial
.
location
.
zone
=
to
.
zone
;
overlayMaterial
.
location
.
sequence
=
to
.
sequence
;
await
eventbus
.
call
(
Task
.
Move
,
overlayMaterial
.
uuid
);
await
callCardMove
(
overlayMaterial
.
uuid
);
}
else
{
console
.
warn
(
`<Move>overlayMaterial from zone=
${
location
.
zone
}
, controller=
${
location
.
controller
}
, sequence=
${
location
.
sequence
}
, overlay_sequence=
${
location
.
overlay_sequence
}
is null`
...
...
@@ -157,15 +157,16 @@ export default async (move: MsgMove) => {
target
.
location
=
to
;
// 维护完了之后,开始动画
await
eventbus
.
call
(
Task
.
Move
,
target
.
uuid
,
from
.
zone
);
const
p
=
callCardMove
(
target
.
uuid
,
{
fromZone
:
from
.
zone
}
);
// 如果from或者to是手卡,那么需要刷新除了这张卡之外,这个玩家的所有手卡
if
([
from
.
zone
,
to
.
zone
].
includes
(
HAND
))
{
await
Promise
.
all
(
cardStore
.
at
(
HAND
,
target
.
location
.
controller
)
.
filter
((
c
)
=>
c
.
uuid
!==
target
.
uuid
)
.
map
(
async
(
c
)
=>
await
eventbus
.
call
(
Task
.
Move
,
c
.
uuid
))
);
const
pHands
=
cardStore
.
at
(
HAND
,
target
.
location
.
controller
)
.
filter
((
c
)
=>
c
.
uuid
!==
target
.
uuid
)
.
map
(
async
(
c
)
=>
await
callCardMove
(
c
.
uuid
));
await
Promise
.
all
([
p
,
...
pHands
]);
}
else
{
await
p
;
}
// 超量素材位置跟随超量怪兽移动
...
...
@@ -180,7 +181,7 @@ export default async (move: MsgMove) => {
overlay
.
location
.
sequence
=
to
.
sequence
;
overlay
.
location
.
position
=
to
.
position
;
await
eventbus
.
call
(
Task
.
Move
,
overlay
.
uuid
);
await
callCardMove
(
overlay
.
uuid
);
}
}
};
src/service/duel/posChange.ts
View file @
a754c2c2
import
{
ygopro
}
from
"
@/api
"
;
import
MsgPosChange
=
ygopro
.
StocGameMessage
.
MsgPosChange
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
,
fetchEsHintMeta
}
from
"
@/stores
"
;
import
{
callCardMove
}
from
"
@/ui/Duel/PlayMat/Card
"
;
export
default
async
(
posChange
:
MsgPosChange
)
=>
{
const
{
location
,
controller
,
sequence
}
=
posChange
.
card_info
;
...
...
@@ -10,7 +11,7 @@ export default async (posChange: MsgPosChange) => {
target
.
location
.
position
=
posChange
.
cur_position
;
// TODO: 暂时用`Move`动画,后续可以单独实现一个改变表示形式的动画
await
eventbus
.
call
(
Task
.
Move
,
target
.
uuid
);
await
callCardMove
(
target
.
uuid
);
}
else
{
console
.
warn
(
`<PosChange>target from
${
posChange
.
card_info
}
is null`
);
}
...
...
src/service/duel/shuffleHandExtra.ts
View file @
a754c2c2
import
{
ygopro
}
from
"
@/api
"
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
}
from
"
@/stores
"
;
import
{
callCardMove
}
from
"
@/ui/Duel/PlayMat/Card
"
;
type
MsgShuffleHandExtra
=
ygopro
.
StocGameMessage
.
MsgShuffleHandExtra
;
...
...
@@ -14,27 +14,29 @@ export default async (shuffleHandExtra: MsgShuffleHandExtra) => {
hash
.
get
(
code
)?.
push
(
sequence
);
});
for
(
const
card
of
cards
)
{
const
sequences
=
hash
.
get
(
card
.
code
);
if
(
sequences
!==
undefined
)
{
const
sequence
=
sequences
.
pop
();
if
(
sequence
!==
undefined
)
{
card
.
location
.
sequence
=
sequence
;
hash
.
set
(
card
.
code
,
sequences
);
Promise
.
all
(
cards
.
map
(
async
(
card
)
=>
{
const
sequences
=
hash
.
get
(
card
.
code
);
if
(
sequences
!==
undefined
)
{
const
sequence
=
sequences
.
pop
();
if
(
sequence
!==
undefined
)
{
card
.
location
.
sequence
=
sequence
;
hash
.
set
(
card
.
code
,
sequences
);
// 触发动画
await
eventbus
.
call
(
Task
.
Move
,
card
.
uuid
);
// 触发动画
await
callCardMove
(
card
.
uuid
);
}
else
{
console
.
warn
(
`<ShuffleHandExtra>sequence poped is none, controller=
${
controller
}
, code=
${
card
.
code
}
, sequence=
${
sequence
}
`
);
}
}
else
{
console
.
warn
(
`<ShuffleHandExtra>sequence poped is none, controller=
${
controller
}
, code=
${
card
.
code
}
, sequence=
${
sequence
}
`
`<ShuffleHandExtra>target from records is null, controller=
${
controller
}
, cards=
${
cards
.
map
(
(
card
)
=>
card
.
code
)}
, codes=
${
codes
}
`
);
}
}
else
{
console
.
warn
(
`<ShuffleHandExtra>target from records is null, controller=
${
controller
}
, cards=
${
cards
.
map
(
(
card
)
=>
card
.
code
)}
, codes=
${
codes
}
`
);
}
}
})
);
};
src/service/duel/shuffleSetCard.ts
View file @
a754c2c2
import
{
ygopro
}
from
"
@/api
"
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
}
from
"
@/stores
"
;
import
{
callCardMove
}
from
"
@/ui/Duel/PlayMat/Card
"
;
import
MsgShuffleSetCard
=
ygopro
.
StocGameMessage
.
MsgShuffleSetCard
;
// 后端传过来的`from_locations`的列表是切洗前场上卡的location,它们在列表里面按照切洗后的顺序排列
...
...
@@ -18,33 +18,36 @@ export default async (shuffleSetCard: MsgShuffleSetCard) => {
}
const
count
=
from_locations
.
length
;
for
(
let
i
=
0
;
i
<
count
;
i
++
)
{
const
from
=
from_locations
[
i
];
const
target
=
cardStore
.
at
(
from
.
zone
,
from
.
controller
,
from
.
sequence
);
if
(
target
)
{
// 设置code为0,洗切后的code会由`UpdateData`指定
target
.
code
=
0
;
target
.
meta
.
id
=
0
;
target
.
meta
.
text
.
id
=
0
;
}
else
{
console
.
warn
(
`<ShuffleSetCard>target from
${
from
}
is null`
);
}
// 处理超量
const
overlay_location
=
overlay_locations
[
i
];
if
(
overlay_location
.
zone
>
0
)
{
// 如果没有超量素材,后端会全传0
for
(
const
overlay
of
cardStore
.
findOverlay
(
from
.
zone
,
from
.
controller
,
from
.
sequence
))
{
// 更新sequence
overlay
.
location
.
sequence
=
overlay_location
.
sequence
;
// 渲染动画
await
eventbus
.
call
(
Task
.
Move
,
overlay
.
uuid
);
// 这里其实有个疑惑,如果超量素材也跟着洗切的话,洗切的意义好像就没有了,感觉算是个k社没想好的设计?
Promise
.
all
(
Array
.
from
({
length
:
count
}).
map
(
async
(
_
,
i
)
=>
{
const
from
=
from_locations
[
i
];
const
target
=
cardStore
.
at
(
from
.
zone
,
from
.
controller
,
from
.
sequence
);
if
(
target
)
{
// 设置code为0,洗切后的code会由`UpdateData`指定
target
.
code
=
0
;
target
.
meta
.
id
=
0
;
target
.
meta
.
text
.
id
=
0
;
}
else
{
console
.
warn
(
`<ShuffleSetCard>target from
${
from
}
is null`
);
}
}
}
// 处理超量
const
overlay_location
=
overlay_locations
[
i
];
if
(
overlay_location
.
zone
>
0
)
{
// 如果没有超量素材,后端会全传0
for
(
const
overlay
of
cardStore
.
findOverlay
(
from
.
zone
,
from
.
controller
,
from
.
sequence
))
{
// 更新sequence
overlay
.
location
.
sequence
=
overlay_location
.
sequence
;
// 渲染动画
await
callCardMove
(
overlay
.
uuid
);
// 这里其实有个疑惑,如果超量素材也跟着洗切的话,洗切的意义好像就没有了,感觉算是个k社没想好的设计?
}
}
})
);
};
src/service/duel/swapGraveDeck.ts
View file @
a754c2c2
import
{
ygopro
}
from
"
@/api
"
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
}
from
"
@/stores
"
;
import
{
callCardMove
}
from
"
@/ui/Duel/PlayMat/Card
"
;
import
MsgSwapGraveDeck
=
ygopro
.
StocGameMessage
.
MsgSwapGraveDeck
;
const
{
DECK
,
GRAVE
}
=
ygopro
.
CardZone
;
...
...
@@ -12,11 +12,11 @@ export default async (swapGraveDeck: MsgSwapGraveDeck) => {
for
(
const
card
of
deck
)
{
card
.
location
.
zone
=
GRAVE
;
await
eventbus
.
call
(
Task
.
Move
,
card
.
uuid
);
await
callCardMove
(
card
.
uuid
);
}
for
(
const
card
of
grave
)
{
card
.
location
.
zone
=
DECK
;
await
eventbus
.
call
(
Task
.
Move
,
card
.
uuid
);
await
callCardMove
(
card
.
uuid
);
}
};
src/service/duel/updateData.ts
View file @
a754c2c2
import
{
fetchCard
,
ygopro
}
from
"
@/api
"
;
import
MsgUpdateData
=
ygopro
.
StocGameMessage
.
MsgUpdateData
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
}
from
"
@/stores
"
;
import
{
callCardMove
}
from
"
@/ui/Duel/PlayMat/Card
"
;
import
MsgUpdateData
=
ygopro
.
StocGameMessage
.
MsgUpdateData
;
export
default
async
(
updateData
:
MsgUpdateData
)
=>
{
const
{
player
:
controller
,
zone
,
actions
}
=
updateData
;
if
(
controller
!==
undefined
&&
zone
!==
undefined
&&
actions
!==
undefined
)
{
...
...
@@ -28,7 +27,7 @@ export default async (updateData: MsgUpdateData) => {
// Currently only update position
target
.
location
.
position
=
action
.
location
.
position
;
// animation
await
eventbus
.
call
(
Task
.
Move
,
target
.
uuid
);
await
callCardMove
(
target
.
uuid
);
}
}
if
(
action
?.
type_
>=
0
)
{
...
...
src/ui/Duel/Message/SelectActionsModal/index.tsx
View file @
a754c2c2
...
...
@@ -34,24 +34,11 @@ const defaultProps: Omit<
const
localStore
=
proxy
(
defaultProps
);
export
const
SelectActionsModal
:
React
.
FC
=
()
=>
{
const
{
isOpen
,
isChain
,
min
,
max
,
single
,
selecteds
,
selectables
,
mustSelects
,
cancelable
,
finishable
,
totalLevels
,
overflow
,
}
=
useSnapshot
(
localStore
);
const
snap
=
useSnapshot
(
localStore
);
const
onSubmit
=
(
options
:
Snapshot
<
Option
[]
>
)
=>
{
const
values
=
options
.
map
((
option
)
=>
option
.
response
!
);
if
(
isChain
)
{
if
(
localStore
.
isChain
)
{
sendSelectSingleResponse
(
values
[
0
]);
}
else
{
sendSelectMultiResponse
(
values
);
...
...
@@ -72,17 +59,7 @@ export const SelectActionsModal: React.FC = () => {
return
(
<
SelectCardsModal
{
...
{
isOpen
,
min
,
max
,
single
,
selecteds
,
selectables
,
mustSelects
,
cancelable
,
finishable
,
totalLevels
,
overflow
,
...
snap
,
onSubmit
,
onFinish
,
onCancel
,
...
...
src/ui/Duel/PlayMat/Bg/index.scss
View file @
a754c2c2
...
...
@@ -67,6 +67,54 @@ section#mat {
}
}
}
// 下面应该和moveToOutside、moveToGround对应
.bg-other-blocks
{
&
.op
{
transform
:
rotate
(
180deg
);
}
position
:
absolute
;
--height
:
var
(
--
card-height-o
);
--width
:
calc
(
var
(
--
height
)
*
var
(
--
card-ratio
));
--left
:
calc
(
var
(
--
col-gap
)
*
2
+
var
(
--
block-width
)
*
2
.5
+
var
(
--
block-outside-offset-x
)
+
var
(
--
width
)
/
2
);
--top
:
calc
(
var
(
--
row-gap
)
+
var
(
--
block-height-m
)
+
(
var
(
--
block-height-m
)
-
var
(
--
height
))
/
2
);
.block
{
position
:
absolute
;
transform
:
translate
(
-50%
,
-50%
);
height
:
var
(
--
height
);
width
:
var
(
--
width
);
top
:
var
(
--
top
);
left
:
var
(
--
left
);
}
.field
{
left
:
calc
(
-1
*
var
(
--
left
));
}
.banish
{
top
:
calc
(
var
(
--
top
)
-
var
(
--
row-gap
)
-
var
(
--
height
));
}
.deck
{
--left
:
calc
(
var
(
--
deck-offset-x
)
+
2
*
(
var
(
--
block-width
)
+
var
(
--
col-gap
))
);
left
:
var
(
--
left
);
top
:
calc
(
var
(
--
deck-offset-y
)
+
2
*
var
(
--
block-height-m
)
+
2
*
var
(
--
row-gap
)
);
transform
:
translate
(
-50%
,
-50%
)
rotate
(
calc
(
-1
*
var
(
--
deck-rotate-z
)));
height
:
var
(
--
deck-card-height
);
width
:
calc
(
var
(
--
deck-card-height
)
*
var
(
--
card-ratio
));
&
.extra-deck
{
left
:
calc
(
-1
*
var
(
--
left
));
transform
:
translate
(
-50%
,
-50%
)
rotate
(
var
(
--
deck-rotate-z
));
}
}
}
}
// 被禁用的样式
...
...
@@ -92,8 +140,19 @@ section#mat {
);
display
:
none
;
}
.block.disabled
{
.disabled-cross
{
display
:
block
;
.disabled-cross.show
{
display
:
block
;
}
section
#mat
{
.block.glowing
{
--card-shadow-color
:
#13a1ff
;
box-shadow
:
0
0
3px
3px
var
(
--
card-shadow-color
)
,
0
0
25px
2px
#0099ff
87
;
background
:
var
(
--
card-shadow-color
);
border-radius
:
2px
;
.triangle
{
display
:
none
;
}
}
}
src/ui/Duel/PlayMat/Bg/index.tsx
View file @
a754c2c2
...
...
@@ -5,12 +5,38 @@ import { type INTERNAL_Snapshot as Snapshot, useSnapshot } from "valtio";
import
{
sendSelectPlaceResponse
,
ygopro
}
from
"
@/api
"
;
import
{
BlockState
,
type
BlockState
,
cardStore
,
isMe
,
type
PlaceInteractivity
,
placeStore
,
}
from
"
@/stores
"
;
const
BgBlock
:
React
.
FC
<
React
.
HTMLProps
<
HTMLDivElement
>
&
{
disabled
?:
boolean
;
highlight
?:
boolean
;
glowing
?:
boolean
;
}
>
=
({
disabled
=
false
,
highlight
=
false
,
glowing
=
false
,
className
,
...
rest
})
=>
(
<
div
{
...
rest
}
className=
{
classnames
(
"
block
"
,
className
,
{
highlight
,
glowing
,
})
}
>
{
<
DecoTriangles
/>
}
{
<
DisabledCross
disabled=
{
disabled
}
/>
}
</
div
>
);
const
BgExtraRow
:
React
.
FC
<
{
meSnap
:
Snapshot
<
BlockState
[]
>
;
opSnap
:
Snapshot
<
BlockState
[]
>
;
...
...
@@ -18,60 +44,74 @@ const BgExtraRow: React.FC<{
return
(
<
div
className=
{
classnames
(
"
bg-row
"
)
}
>
{
Array
.
from
({
length
:
2
}).
map
((
_
,
i
)
=>
(
<
div
<
BgBlock
key=
{
i
}
className=
{
classnames
(
"
block
"
,
"
extra
"
,
{
highlight
:
!!
meSnap
[
i
].
interactivity
||
!!
opSnap
[
i
].
interactivity
,
disabled
:
meSnap
[
i
].
disabled
||
opSnap
[
i
].
disabled
,
})
}
className=
"extra"
onClick=
{
()
=>
{
onBlockClick
(
meSnap
[
i
].
interactivity
);
onBlockClick
(
opSnap
[
i
].
interactivity
);
}
}
>
{
<
DecoTriangles
/>
}
{
<
DisabledCross
/>
}
</
div
>
disabled=
{
meSnap
[
i
].
disabled
||
opSnap
[
i
].
disabled
}
highlight=
{
!!
meSnap
[
i
].
interactivity
||
!!
opSnap
[
i
].
interactivity
}
/>
))
}
</
div
>
);
};
const
BgRow
:
React
.
FC
<
{
isS
zone
?:
boolean
;
s
zone
?:
boolean
;
opponent
?:
boolean
;
snap
:
Snapshot
<
BlockState
[]
>
;
}
>
=
({
isS
zone
=
false
,
opponent
=
false
,
snap
})
=>
(
}
>
=
({
s
zone
=
false
,
opponent
=
false
,
snap
})
=>
(
<
div
className=
{
classnames
(
"
bg-row
"
,
{
opponent
})
}
>
{
Array
.
from
({
length
:
5
}).
map
((
_
,
i
)
=>
(
<
div
<
BgBlock
key=
{
i
}
className=
{
classnames
(
"
block
"
,
{
szone
:
isSzone
,
highlight
:
!!
snap
[
i
].
interactivity
,
disabled
:
snap
[
i
].
disabled
,
})
}
className=
{
classnames
({
szone
})
}
onClick=
{
()
=>
onBlockClick
(
snap
[
i
].
interactivity
)
}
>
{
<
DecoTriangles
/>
}
{
<
DisabledCross
/>
}
</
div
>
disabled=
{
snap
[
i
].
disabled
}
highlight=
{
!!
snap
[
i
].
interactivity
}
/>
))
}
</
div
>
);
const
BgOtherBlocks
:
React
.
FC
<
{
me
?:
boolean
}
>
=
({
me
})
=>
{
useSnapshot
(
cardStore
);
const
meController
=
isMe
(
0
)
?
0
:
1
;
const
judgeGlowing
=
(
zone
:
ygopro
.
CardZone
)
=>
!!
cardStore
.
at
(
zone
,
meController
)
.
reduce
((
sum
,
c
)
=>
(
sum
+=
c
.
idleInteractivities
.
length
),
0
);
const
glowingExtra
=
judgeGlowing
(
ygopro
.
CardZone
.
EXTRA
);
const
glowingGraveyard
=
judgeGlowing
(
ygopro
.
CardZone
.
GRAVE
);
const
glowingBanish
=
judgeGlowing
(
ygopro
.
CardZone
.
REMOVED
);
return
(
<
div
className=
{
classnames
(
"
bg-other-blocks
"
,
{
me
,
op
:
!
me
})
}
>
<
BgBlock
className=
"banish"
glowing=
{
me
&&
glowingBanish
}
/>
<
BgBlock
className=
"graveyard"
glowing=
{
me
&&
glowingGraveyard
}
/>
<
BgBlock
className=
"field"
/>
<
BgBlock
className=
"deck"
/>
<
BgBlock
className=
"deck extra-deck"
glowing=
{
me
&&
glowingExtra
}
/>
</
div
>
);
};
export
const
Bg
:
React
.
FC
=
()
=>
{
const
snap
=
useSnapshot
(
placeStore
.
inner
);
return
(
<
div
className=
"mat-bg"
>
<
BgRow
snap=
{
snap
[
ygopro
.
CardZone
.
SZONE
].
op
}
isS
zone
opponent
/>
<
BgRow
snap=
{
snap
[
ygopro
.
CardZone
.
SZONE
].
op
}
s
zone
opponent
/>
<
BgRow
snap=
{
snap
[
ygopro
.
CardZone
.
MZONE
].
op
}
opponent
/>
<
BgExtraRow
meSnap=
{
snap
[
ygopro
.
CardZone
.
MZONE
].
me
.
slice
(
5
,
7
)
}
opSnap=
{
snap
[
ygopro
.
CardZone
.
MZONE
].
op
.
slice
(
5
,
7
)
}
/>
<
BgRow
snap=
{
snap
[
ygopro
.
CardZone
.
MZONE
].
me
}
/>
<
BgRow
snap=
{
snap
[
ygopro
.
CardZone
.
SZONE
].
me
}
isSzone
/>
<
BgRow
snap=
{
snap
[
ygopro
.
CardZone
.
SZONE
].
me
}
szone
/>
<
BgOtherBlocks
me
/>
<
BgOtherBlocks
/>
</
div
>
);
};
...
...
@@ -92,4 +132,6 @@ const DecoTriangles: React.FC = () => (
</>
);
const
DisabledCross
:
React
.
FC
=
()
=>
<
div
className=
"disabled-cross"
></
div
>;
const
DisabledCross
:
React
.
FC
<
{
disabled
:
boolean
}
>
=
({
disabled
})
=>
(
<
div
className=
{
classnames
(
"
disabled-cross
"
,
{
show
:
disabled
})
}
></
div
>
);
src/ui/Duel/PlayMat/Card/index.scss
View file @
a754c2c2
...
...
@@ -106,7 +106,7 @@ section#mat {
}
}
.mat-card.
highlight
.card-shadow
{
.mat-card.
glowing
.card-shadow
{
--card-shadow-color
:
#0099ff
;
display
:
block
!
important
;
background
:
var
(
--
card-shadow-color
)
!
important
;
...
...
@@ -133,3 +133,7 @@ section#mat {
}
text-align
:
center
;
}
.card-dropdown-disabled
{
display
:
none
;
}
src/ui/Duel/PlayMat/Card/index.tsx
View file @
a754c2c2
...
...
@@ -26,23 +26,20 @@ import {
import
{
interactTypeToString
}
from
"
../../utils
"
;
import
{
attack
,
type
AttackOptions
,
focus
,
moveToDeck
,
moveToGround
,
moveToHand
,
moveToOutside
,
moveToToken
,
move
,
type
MoveOptions
,
}
from
"
./springs
"
;
import
type
{
SpringApiProps
}
from
"
./springs/types
"
;
const
{
HAND
,
GRAVE
,
REMOVED
,
DECK
,
EXTRA
,
MZONE
,
SZONE
,
TZONE
}
=
ygopro
.
CardZone
;
const
{
HAND
,
GRAVE
,
REMOVED
,
EXTRA
,
MZONE
,
SZONE
,
TZONE
}
=
ygopro
.
CardZone
;
export
const
Card
:
React
.
FC
<
{
idx
:
number
}
>
=
React
.
memo
(({
idx
})
=>
{
const
state
=
cardStore
.
inner
[
idx
];
const
snap
=
useSnapshot
(
state
);
const
card
=
cardStore
.
inner
[
idx
];
const
snap
=
useSnapshot
(
card
);
const
[
styles
,
api
]
=
useSpring
(
const
[
styles
,
api
]
=
useSpring
<
SpringApiProps
>
(
()
=>
({
x
:
0
,
...
...
@@ -57,42 +54,17 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
focusDisplay
:
"
none
"
,
focusOpacity
:
1
,
subZ
:
0
,
opacity
:
1
,
}
satisfies
SpringApiProps
)
);
// FIXME: move不应该只根据目的地判断,还要根据先前的位置判断。例子是Token。
const
move
=
async
(
toZone
:
ygopro
.
CardZone
,
fromZone
?:
ygopro
.
CardZone
)
=>
{
switch
(
toZone
)
{
case
MZONE
:
case
SZONE
:
await
moveToGround
({
card
:
state
,
api
,
fromZone
});
break
;
case
HAND
:
await
moveToHand
({
card
:
state
,
api
,
fromZone
});
break
;
case
DECK
:
case
EXTRA
:
await
moveToDeck
({
card
:
state
,
api
,
fromZone
});
break
;
case
GRAVE
:
case
REMOVED
:
await
moveToOutside
({
card
:
state
,
api
,
fromZone
});
break
;
case
TZONE
:
// FIXME: 这里应该实现一个衍生物消散的动画,现在暂时让它在动画在展示上回到卡组
await
moveToToken
({
card
:
state
,
api
,
fromZone
});
break
;
}
};
// 每张卡都需要移动到初始位置
useEffect
(()
=>
{
move
(
state
.
location
.
zone
);
addToAnimation
(()
=>
move
({
card
,
api
})
);
},
[]);
const
[
highlight
,
setHighlight
]
=
useState
(
false
);
const
[
glowing
,
setGrowing
]
=
useState
(
false
);
const
[
classFocus
,
setClassFocus
]
=
useState
(
false
);
// const [shadowOpacity, setShadowOpacity] = useState(0); // TODO: 透明度
// >>> 动画 >>>
/** 动画序列的promise */
...
...
@@ -103,40 +75,32 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
animationQueue
.
current
=
animationQueue
.
current
.
then
(
p
).
then
(
rs
);
});
const
register
=
<
T
extends
any
[]
>
(
task: Task,
fn: (...args: T) =
>
Promise
<
unknown
>
) =
>
{
eventbus
.
register
(
task
,
async
(
uuid
,
...
rest
:
T
)
=>
{
if
(
uuid
===
card
.
uuid
)
{
await
fn
(...
rest
);
return
true
;
}
else
return
false
;
});
}
;
useEffect(() =
>
{
eventbus
.
register
(
Task
.
Move
,
async
(
uuid
:
string
,
fromZone
?:
ygopro
.
CardZone
)
=>
{
if
(
uuid
===
state
.
uuid
)
{
await
addToAnimation
(()
=>
move
(
state
.
location
.
zone
,
fromZone
));
}
}
);
register
(
Task
.
Move
,
async
(
options
?:
MoveOptions
)
=>
{
await
addToAnimation
(()
=>
move
({
card
,
api
,
options
}));
});
eventbus
.
register
(
Task
.
Focus
,
async
(
uuid
:
string
)
=>
{
if
(
uuid
===
state
.
uuid
)
{
await
addToAnimation
(
async
()
=>
{
setClassFocus
(
true
);
setTimeout
(()
=>
setClassFocus
(
false
),
1000
);
await
focus
({
card
:
state
,
api
});
});
}
register
(
Task
.
Focus
,
async
()
=>
{
setClassFocus
(
true
);
setTimeout
(()
=>
setClassFocus
(
false
),
1000
);
// TODO: 这儿为啥要这么写呢
await
focus
({
card
,
api
});
});
eventbus
.
register
(
Task
.
Attack
,
async
(
uuid
:
string
,
directAttack
:
boolean
,
target
?:
ygopro
.
CardLocation
)
=>
{
if
(
uuid
===
state
.
uuid
)
{
await
addToAnimation
(()
=>
attack
({
card
:
state
,
api
,
target
,
directAttack
})
);
}
}
);
register
(
Task
.
Attack
,
async
(
options
:
AttackOptions
)
=>
{
await
addToAnimation
(()
=>
attack
({
card
,
api
,
options
}));
});
}
, []);
//
<
<<
动画
<<<
...
...
@@ -144,13 +108,19 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
// >>> 效果 >>>
const
idleInteractivities
=
snap
.
idleInteractivities
;
useEffect
(()
=
>
{
setHighlight
(
!!
idleInteractivities
.
length
);
setGrowing
(
!!
idleInteractivities
.
length
&&
[
MZONE
,
SZONE
,
HAND
,
TZONE
].
includes
(
card
.
location
.
zone
)
);
}
, [idleInteractivities]);
const [dropdownMenu, setDropdownMenu] = useState(
{
items
:
[]
as
DropdownItem
[],
}
);
// 是否禁用下拉菜单
const [dropdownMenuDisabled, setDropdownMenuDisabled] = useState(false);
// 发动效果
// 1. 下拉菜单里面选择[召唤 / 特殊召唤 /.../效果发动]
// 2. 如果是非效果发动,那么直接选择哪张卡(单张卡直接选择那张)
...
...
@@ -165,6 +135,13 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
map
.
get
(
interactType
)?.
push
(
card
);
});
});
if
(
!
map
.
size
)
{
setDropdownMenuDisabled
(
true
);
return
;
}
else
{
setDropdownMenuDisabled
(
false
);
}
const
actions
=
[...
map
.
entries
()];
const
nonEffectActions
=
actions
.
filter
(
([
action
])
=>
action
!==
InteractType
.
ACTIVATE
...
...
@@ -259,7 +236,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
// 中央弹窗展示选中卡牌信息
// TODO: 同一张卡片,是否重复点击会关闭CardModal?
displayCardModal
(
card
);
if
(
card
.
idleInteractivities
.
length
)
handleDropdownMenu
([
card
],
false
);
handleDropdownMenu
([
card
],
false
);
// 侧边栏展示超量素材信息
const
overlayMaterials
=
cardStore
.
findOverlay
(
...
...
@@ -286,17 +263,17 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
handleDropdownMenu
(
cards
,
true
);
};
if
([
MZONE
,
SZONE
,
HAND
].
includes
(
state
.
location
.
zone
))
{
onCardClick
(
state
);
}
else
if
([
EXTRA
,
GRAVE
,
REMOVED
].
includes
(
state
.
location
.
zone
))
{
onFieldClick
(
state
);
if
([
MZONE
,
SZONE
,
HAND
].
includes
(
card
.
location
.
zone
))
{
onCardClick
(
card
);
}
else
if
([
EXTRA
,
GRAVE
,
REMOVED
].
includes
(
card
.
location
.
zone
))
{
onFieldClick
(
card
);
}
}
;
//
<
<<
效果
<<<
return
(
<
animated
.
div
className=
{
classnames
(
"
mat-card
"
,
{
highlight
})
}
className=
{
classnames
(
"
mat-card
"
,
{
glowing
})
}
style=
{
{
transform
:
to
(
...
...
@@ -312,6 +289,7 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
"
--focus-scale
"
:
styles
.
focusScale
,
"
--focus-display
"
:
styles
.
focusDisplay
,
"
--focus-opacity
"
:
styles
.
focusOpacity
,
opacity
:
styles
.
opacity
,
}
as
any
as
CSSProperties
}
onClick=
{
onClick
}
...
...
@@ -321,10 +299,11 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
<
Dropdown
menu=
{
dropdownMenu
}
placement=
"top"
overlayClassName=
"card-dropdown"
overlayClassName=
{
classnames
(
"
card-dropdown
"
,
{
"
card-dropdown-disabled
"
:
dropdownMenuDisabled
,
})
}
arrow
trigger=
{
[
"
click
"
]
}
// disabled={!highlight} // TODO: 这里的disable要考虑到field的情况,比如额外卡组
>
<
div
className=
{
classnames
(
"
card-img-wrap
"
,
{
focus
:
classFocus
})
}
>
<
YgoCard
...
...
@@ -375,3 +354,12 @@ const handleEffectActivation = (
}
;
//
<
<<
下拉菜单
<<<
const
call
=
<
Options
,
>
(task: Task) =
>
(uuid: string, options?: Options extends
{}
? Options : never) =
>
eventbus.call(task, uuid, options);
export const callCardMove = call
<
MoveOptions
>
(Task.Move);
export const callCardFocus = call(Task.Focus);
export const callCardAttack = call
<
AttackOptions
>
(Task.Attack);
src/ui/Duel/PlayMat/Card/springs/attack.ts
View file @
a754c2c2
// 暂时先简单实现攻击动画,后面有时间再慢慢优化
import
{
easings
}
from
"
@react-spring/web
"
;
import
{
ygopro
}
from
"
@/api
"
;
import
{
CardType
,
isMe
}
from
"
@/stores
"
;
import
{
isMe
}
from
"
@/stores
"
;
import
{
matConfig
}
from
"
@/ui/Shared
"
;
import
{
matConfig
}
from
"
../../utils
"
;
import
type
{
SpringApi
}
from
"
./types
"
;
import
type
{
AttackFunc
}
from
"
./types
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
COL_GAP
,
ROW_GAP
}
=
matConfig
;
export
const
attack
=
async
(
props
:
{
card
:
CardType
;
api
:
SpringApi
;
directAttack
:
boolean
;
target
?:
ygopro
.
CardLocation
;
})
=>
{
const
{
card
,
api
,
directAttack
,
target
}
=
props
;
export
const
attack
:
AttackFunc
=
async
(
props
)
=>
{
const
{
card
,
api
,
options
}
=
props
;
const
current
=
api
.
current
[
0
].
get
();
let
x
=
current
.
x
;
let
y
=
current
.
y
;
let
rz
=
current
.
rz
;
if
(
directAttack
)
{
if
(
options
?.
directAttack
)
{
// 直接攻击
y
=
BLOCK_HEIGHT_M
.
value
+
BLOCK_HEIGHT_S
.
value
;
y
=
BLOCK_HEIGHT_M
+
BLOCK_HEIGHT_S
;
if
(
isMe
(
card
.
location
.
controller
))
{
y
=
-
y
;
}
}
else
if
(
target
)
{
}
else
if
(
options
?.
target
)
{
// 攻击`target`
const
{
controller
,
sequence
}
=
target
;
const
{
controller
,
sequence
}
=
options
.
target
;
if
(
sequence
>
4
)
{
// 额外怪兽区
x
=
(
sequence
>
5
?
1
:
-
1
)
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
x
=
(
sequence
>
5
?
1
:
-
1
)
*
(
BLOCK_WIDTH
+
COL_GAP
);
y
=
0
;
}
else
{
x
=
(
sequence
-
2
)
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
y
=
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
;
x
=
(
sequence
-
2
)
*
(
BLOCK_WIDTH
+
COL_GAP
);
y
=
BLOCK_HEIGHT_M
+
ROW_GAP
;
}
if
(
!
isMe
(
controller
))
{
...
...
@@ -61,7 +55,7 @@ export const attack = async (props: {
await
asyncStart
(
api
)({
y
:
current
.
y
+
(
BLOCK_HEIGHT_M
.
value
/
2
)
*
(
isMe
(
card
.
location
.
controller
)
?
1
:
-
1
),
(
BLOCK_HEIGHT_M
/
2
)
*
(
isMe
(
card
.
location
.
controller
)
?
1
:
-
1
),
rz
,
});
// 加速前冲
...
...
src/ui/Duel/PlayMat/Card/springs/focus.ts
View file @
a754c2c2
...
...
@@ -11,19 +11,14 @@ export const focus = async (props: { card: CardType; api: SpringApi }) => {
card
.
location
.
zone
==
ygopro
.
CardZone
.
HAND
||
card
.
location
.
zone
==
ygopro
.
CardZone
.
DECK
)
{
const
current
=
api
.
current
[
0
].
get
()
;
const
current
=
{
...
api
.
current
[
0
].
get
()
}
;
await
asyncStart
(
api
)({
y
:
current
.
y
+
(
matStore
.
isMe
(
card
.
location
.
controller
)
?
-
1
:
1
)
*
120
,
// TODO: 放到config之中
ry
:
0
,
rz
:
0
,
//
rz: 0,
z
:
current
.
z
+
50
,
});
await
asyncStart
(
api
)({
y
:
current
.
y
,
ry
:
current
.
ry
,
rz
:
current
.
rz
,
z
:
current
.
z
,
});
await
asyncStart
(
api
)(
current
);
}
else
{
await
asyncStart
(
api
)({
focusScale
:
1.5
,
...
...
src/ui/Duel/PlayMat/Card/springs/index.ts
View file @
a754c2c2
export
*
from
"
./attack
"
;
export
*
from
"
./focus
"
;
export
*
from
"
./moveToDeck
"
;
export
*
from
"
./moveToGround
"
;
export
*
from
"
./moveToHand
"
;
export
*
from
"
./moveToOutside
"
;
export
*
from
"
./moveToToken
"
;
export
*
from
"
./move
"
;
export
*
from
"
./types
"
;
export
*
from
"
./utils
"
;
src/ui/Duel/PlayMat/Card/springs/move.ts
0 → 100644
View file @
a754c2c2
import
{
ygopro
}
from
"
@/api
"
;
import
{
moveToDeck
}
from
"
./moveToDeck
"
;
import
{
moveToGround
}
from
"
./moveToGround
"
;
import
{
moveToHand
}
from
"
./moveToHand
"
;
import
{
moveToOutside
}
from
"
./moveToOutside
"
;
import
{
moveToToken
}
from
"
./moveToToken
"
;
import
type
{
MoveFunc
}
from
"
./types
"
;
const
{
HAND
,
GRAVE
,
REMOVED
,
DECK
,
EXTRA
,
MZONE
,
SZONE
,
TZONE
}
=
ygopro
.
CardZone
;
export
const
move
:
MoveFunc
=
async
(
props
)
=>
{
const
{
card
}
=
props
;
switch
(
card
.
location
.
zone
)
{
case
MZONE
:
case
SZONE
:
await
moveToGround
(
props
);
break
;
case
HAND
:
await
moveToHand
(
props
);
break
;
case
DECK
:
case
EXTRA
:
await
moveToDeck
(
props
);
break
;
case
GRAVE
:
case
REMOVED
:
await
moveToOutside
(
props
);
break
;
case
TZONE
:
await
moveToToken
(
props
);
break
;
}
};
src/ui/Duel/PlayMat/Card/springs/moveToDeck.ts
View file @
a754c2c2
import
{
ygopro
}
from
"
@/api
"
;
import
{
isMe
}
from
"
@/stores
"
;
import
{
matConfig
}
from
"
@/ui/Shared
"
;
import
{
matConfig
}
from
"
../../util
s
"
;
import
{
asyncStart
,
type
MoveFunc
}
from
"
./utils
"
;
import
type
{
MoveFunc
}
from
"
./type
s
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
COL_GAP
,
ROW_GAP
,
DECK_OFFSET_X
,
...
...
@@ -24,21 +24,16 @@ export const moveToDeck: MoveFunc = async (props) => {
const
{
location
}
=
card
;
const
{
controller
,
zone
,
sequence
}
=
location
;
const
rightX
=
DECK_OFFSET_X
.
value
+
2
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
const
rightX
=
DECK_OFFSET_X
+
2
*
(
BLOCK_WIDTH
+
COL_GAP
);
const
leftX
=
-
rightX
;
const
bottomY
=
DECK_OFFSET_Y
.
value
+
2
*
BLOCK_HEIGHT_M
.
value
+
BLOCK_HEIGHT_S
.
value
+
2
*
ROW_GAP
.
value
-
BLOCK_HEIGHT_S
.
value
;
const
bottomY
=
DECK_OFFSET_Y
+
2
*
BLOCK_HEIGHT_M
+
2
*
ROW_GAP
;
const
topY
=
-
bottomY
;
let
x
=
isMe
(
controller
)
?
rightX
:
leftX
;
let
y
=
isMe
(
controller
)
?
bottomY
:
topY
;
if
(
zone
===
EXTRA
)
{
x
=
isMe
(
controller
)
?
leftX
:
rightX
;
}
let
rz
=
zone
===
EXTRA
?
DECK_ROTATE_Z
.
value
:
-
DECK_ROTATE_Z
.
value
;
let
rz
=
zone
===
EXTRA
?
DECK_ROTATE_Z
:
-
DECK_ROTATE_Z
;
rz
+=
isMe
(
controller
)
?
0
:
180
;
const
z
=
sequence
;
...
...
@@ -49,6 +44,6 @@ export const moveToDeck: MoveFunc = async (props) => {
rz
,
ry
:
isMe
(
controller
)
?
(
zone
===
DECK
?
180
:
0
)
:
180
,
zIndex
:
z
,
height
:
DECK_CARD_HEIGHT
.
value
,
height
:
DECK_CARD_HEIGHT
,
});
};
src/ui/Duel/PlayMat/Card/springs/moveToGround.ts
View file @
a754c2c2
...
...
@@ -2,9 +2,10 @@ import { easings } from "@react-spring/web";
import
{
ygopro
}
from
"
@/api
"
;
import
{
isMe
}
from
"
@/stores
"
;
import
{
matConfig
}
from
"
@/ui/Shared
"
;
import
{
matConfig
}
from
"
../../util
s
"
;
import
{
asyncStart
,
type
MoveFunc
}
from
"
./utils
"
;
import
type
{
MoveFunc
}
from
"
./type
s
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
BLOCK_WIDTH
,
...
...
@@ -13,24 +14,20 @@ const {
CARD_RATIO
,
COL_GAP
,
ROW_GAP
,
BLOCK_OUTSIDE_OFFSET_X
,
CARD_HEIGHT_O
,
}
=
matConfig
;
const
{
MZONE
,
SZONE
,
TZONE
}
=
ygopro
.
CardZone
;
export
const
moveToGround
:
MoveFunc
=
async
(
props
)
=>
{
const
{
card
,
api
,
fromZone
}
=
props
;
const
{
card
,
api
,
options
}
=
props
;
const
{
location
}
=
card
;
const
{
controller
,
zone
,
sequence
,
position
,
is_overlay
}
=
location
;
// 根据zone计算卡片的宽度
const
cardWidth
=
zone
===
SZONE
?
BLOCK_HEIGHT_S
.
value
*
CARD_RATIO
.
value
:
BLOCK_HEIGHT_M
.
value
*
CARD_RATIO
.
value
;
let
height
=
zone
===
SZONE
?
BLOCK_HEIGHT_S
.
value
:
BLOCK_HEIGHT_M
.
value
;
let
height
=
zone
===
SZONE
?
BLOCK_HEIGHT_S
:
BLOCK_HEIGHT_M
;
// 首先计算 x 和 y
let
x
=
0
,
...
...
@@ -38,28 +35,31 @@ export const moveToGround: MoveFunc = async (props) => {
switch
(
zone
)
{
case
SZONE
:
{
if
(
sequence
===
5
)
{
height
=
CARD_HEIGHT_O
;
// 场地魔法
x
=
-
(
3
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
)
-
(
BLOCK_WIDTH
.
value
-
cardWidth
)
/
2
BLOCK_WIDTH
*
2.5
+
COL_GAP
*
2
+
BLOCK_OUTSIDE_OFFSET_X
+
CARD_HEIGHT_O
*
CARD_RATIO
*
0.5
);
y
=
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
;
y
=
ROW_GAP
+
BLOCK_HEIGHT_M
+
(
BLOCK_HEIGHT_M
-
CARD_HEIGHT_O
)
/
2
;
}
else
{
x
=
(
sequence
-
2
)
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
x
=
(
sequence
-
2
)
*
(
BLOCK_WIDTH
+
COL_GAP
);
y
=
2
*
(
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
)
-
(
BLOCK_HEIGHT_M
.
value
-
BLOCK_HEIGHT_S
.
value
)
/
2
;
2
*
(
BLOCK_HEIGHT_M
+
ROW_GAP
)
-
(
BLOCK_HEIGHT_M
-
BLOCK_HEIGHT_S
)
/
2
;
}
break
;
}
case
MZONE
:
{
if
(
sequence
>
4
)
{
// 额外怪兽区
x
=
(
sequence
>
5
?
1
:
-
1
)
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
x
=
(
sequence
>
5
?
1
:
-
1
)
*
(
BLOCK_WIDTH
+
COL_GAP
);
y
=
0
;
}
else
{
x
=
(
sequence
-
2
)
*
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
);
y
=
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
;
x
=
(
sequence
-
2
)
*
(
BLOCK_WIDTH
+
COL_GAP
);
y
=
BLOCK_HEIGHT_M
+
ROW_GAP
;
}
break
;
}
...
...
@@ -76,9 +76,8 @@ export const moveToGround: MoveFunc = async (props) => {
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
,
ygopro
.
CardPosition
.
FACEUP_DEFENSE
,
].
includes
(
position
??
5
);
height
=
defence
?
BLOCK_WIDTH
.
value
:
height
;
let
rz
=
isMe
(
controller
)
?
0
:
180
;
rz
+=
defence
?
90
:
0
;
height
=
defence
?
BLOCK_WIDTH
:
height
;
const
rz
=
(
isMe
(
controller
)
?
0
:
180
)
+
(
defence
?
90
:
0
);
const
ry
=
[
ygopro
.
CardPosition
.
FACEDOWN
,
...
...
@@ -89,7 +88,8 @@ export const moveToGround: MoveFunc = async (props) => {
:
0
;
// 动画
if
(
fromZone
===
TZONE
)
{
const
isToken
=
options
?.
fromZone
===
TZONE
;
if
(
isToken
)
{
// 如果是Token,直接先移动到那个位置,然后再放大
api
.
set
({
x
,
...
...
@@ -107,8 +107,9 @@ export const moveToGround: MoveFunc = async (props) => {
ry
,
rz
,
config
:
{
// mass: 0.5,
easing
:
easings
.
easeInOutSine
,
tension
:
250
,
clamp
:
true
,
easing
:
easings
.
easeOutSine
,
},
});
}
...
...
@@ -116,13 +117,12 @@ export const moveToGround: MoveFunc = async (props) => {
await
asyncStart
(
api
)({
height
,
z
:
0
,
subZ
:
isToken
?
100
:
0
,
zIndex
:
is_overlay
?
1
:
3
,
config
:
{
easing
:
easings
.
easeInOutQuad
,
mass
:
5
,
tension
:
300
,
// 170
friction
:
12
,
// 26
easing
:
easings
.
easeInQuad
,
clamp
:
true
,
},
});
if
(
isToken
)
api
.
set
({
subZ
:
0
});
};
src/ui/Duel/PlayMat/Card/springs/moveToHand.ts
View file @
a754c2c2
import
{
ygopro
}
from
"
@/api
"
;
import
{
cardStore
,
isMe
}
from
"
@/stores
"
;
import
{
matConfig
}
from
"
@/ui/Shared
"
;
import
{
matConfig
}
from
"
../../util
s
"
;
import
{
asyncStart
,
type
MoveFunc
}
from
"
./utils
"
;
import
type
{
MoveFunc
}
from
"
./type
s
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
BLOCK_HEIGHT_M
,
...
...
@@ -22,27 +23,23 @@ export const moveToHand: MoveFunc = async (props) => {
// 手卡会有很复杂的计算...
const
hand_circle_center_x
=
0
;
const
hand_circle_center_y
=
1
*
BLOCK_HEIGHT_M
.
value
+
1
*
BLOCK_HEIGHT_S
.
value
+
2
*
ROW_GAP
.
value
+
(
HAND_MARGIN_TOP
.
value
+
HAND_CARD_HEIGHT
.
value
+
HAND_CIRCLE_CENTER_OFFSET_Y
.
value
);
const
hand_card_width
=
CARD_RATIO
.
value
*
HAND_CARD_HEIGHT
.
value
;
BLOCK_HEIGHT_M
+
BLOCK_HEIGHT_S
+
2
*
ROW_GAP
+
(
HAND_MARGIN_TOP
+
HAND_CARD_HEIGHT
+
HAND_CIRCLE_CENTER_OFFSET_Y
);
const
hand_card_width
=
CARD_RATIO
*
HAND_CARD_HEIGHT
;
const
THETA
=
2
*
Math
.
atan
(
hand_card_width
/
2
/
(
HAND_CIRCLE_CENTER_OFFSET_Y
.
value
+
HAND_CARD_HEIGHT
.
value
)
hand_card_width
/
2
/
(
HAND_CIRCLE_CENTER_OFFSET_Y
+
HAND_CARD_HEIGHT
)
)
*
0.9
;
// 接下来计算每一张手卡
const
hands_length
=
cardStore
.
at
(
HAND
,
controller
).
length
;
const
angle
=
(
sequence
-
(
hands_length
-
1
)
/
2
)
*
THETA
;
const
r
=
HAND_CIRCLE_CENTER_OFFSET_Y
.
value
+
HAND_CARD_HEIGHT
.
value
/
2
;
const
r
=
HAND_CIRCLE_CENTER_OFFSET_Y
+
HAND_CARD_HEIGHT
/
2
;
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
/
2
;
const
x
=
hand_circle_center_x
+
negativeX
*
(
isMe
(
controller
)
?
1
:
-
1
);
const
y
=
hand_circle_center_y
-
negativeY
+
140
;
// FIXME: 常量 是手动调的 这里肯定有问题 有空来修
...
...
@@ -54,8 +51,8 @@ export const moveToHand: MoveFunc = async (props) => {
z
:
sequence
+
5
,
rz
:
isMe
(
controller
)
?
_rz
:
180
-
_rz
,
ry
:
isMe
(
controller
)
?
0
:
180
,
height
:
HAND_CARD_HEIGHT
.
value
,
height
:
HAND_CARD_HEIGHT
,
zIndex
:
sequence
,
// rx: -PLANE_ROTATE_X
.value
,
// rx: -PLANE_ROTATE_X,
});
};
src/ui/Duel/PlayMat/Card/springs/moveToOutside.ts
View file @
a754c2c2
import
{
ygopro
}
from
"
@/api
"
;
import
{
isMe
}
from
"
@/stores
"
;
import
{
matConfig
}
from
"
@/ui/Shared
"
;
import
{
matConfig
}
from
"
../../util
s
"
;
import
{
asyncStart
,
type
MoveFunc
}
from
"
./utils
"
;
import
type
{
MoveFunc
}
from
"
./type
s
"
;
import
{
asyncStart
}
from
"
./utils
"
;
const
{
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
BLOCK_HEIGHT_S
,
COL_GAP
,
ROW_GAP
}
=
matConfig
;
const
{
BLOCK_WIDTH
,
BLOCK_HEIGHT_M
,
COL_GAP
,
ROW_GAP
,
CARD_HEIGHT_O
,
BLOCK_OUTSIDE_OFFSET_X
,
CARD_RATIO
,
}
=
matConfig
;
const
{
GRAVE
}
=
ygopro
.
CardZone
;
const
{
REMOVED
}
=
ygopro
.
CardZone
;
export
const
moveToOutside
:
MoveFunc
=
async
(
props
)
=>
{
const
{
card
,
api
}
=
props
;
// report
const
{
zone
,
controller
,
position
,
sequence
}
=
card
.
location
;
let
x
=
(
BLOCK_WIDTH
.
value
+
COL_GAP
.
value
)
*
3
,
y
=
zone
===
GRAVE
?
BLOCK_HEIGHT_M
.
value
+
ROW_GAP
.
value
:
0
;
let
x
=
BLOCK_WIDTH
*
2.5
+
COL_GAP
*
2
+
BLOCK_OUTSIDE_OFFSET_X
+
CARD_HEIGHT_O
*
CARD_RATIO
*
0.5
,
y
=
ROW_GAP
+
BLOCK_HEIGHT_M
+
(
BLOCK_HEIGHT_M
-
CARD_HEIGHT_O
)
/
2
;
if
(
zone
===
REMOVED
)
y
-=
ROW_GAP
+
CARD_HEIGHT_O
;
if
(
!
isMe
(
controller
))
{
x
=
-
x
;
y
=
-
y
;
...
...
@@ -24,11 +36,14 @@ export const moveToOutside: MoveFunc = async (props) => {
x
,
y
,
z
:
0
,
height
:
BLOCK_HEIGHT_S
.
value
,
height
:
CARD_HEIGHT_O
,
rz
:
isMe
(
controller
)
?
0
:
180
,
ry
:
[
ygopro
.
CardPosition
.
FACEDOWN
].
includes
(
position
)
?
180
:
0
,
subZ
:
100
,
zIndex
:
sequence
,
config
:
{
tension
:
140
,
},
});
api
.
set
({
subZ
:
0
});
};
src/ui/Duel/PlayMat/Card/springs/moveToToken.ts
View file @
a754c2c2
import
{
asyncStart
,
type
MoveFunc
}
from
"
./utils
"
;
import
type
{
MoveFunc
}
from
"
./types
"
;
import
{
asyncStart
}
from
"
./utils
"
;
export
const
moveToToken
:
MoveFunc
=
async
(
props
)
=>
{
const
{
api
}
=
props
;
await
asyncStart
(
api
)({
height
:
0
,
opacity
:
0
,
});
api
.
set
({
opacity
:
1
});
};
src/ui/Duel/PlayMat/Card/springs/types.ts
View file @
a754c2c2
import
{
type
SpringRef
}
from
"
@react-spring/web
"
;
import
type
{
SpringRef
}
from
"
@react-spring/web
"
;
import
type
{
ygopro
}
from
"
@/api
"
;
import
type
{
CardType
}
from
"
@/stores
"
;
export
interface
SpringApiProps
{
x
:
number
;
...
...
@@ -9,6 +12,7 @@ export interface SpringApiProps {
rz
:
number
;
zIndex
:
number
;
height
:
number
;
opacity
:
number
;
// >>> focus
focusScale
:
number
;
focusDisplay
:
string
;
...
...
@@ -19,3 +23,21 @@ export interface SpringApiProps {
}
export
type
SpringApi
=
SpringRef
<
SpringApiProps
>
;
type
OptionsToFunc
<
Options
>
=
(
props
:
{
card
:
CardType
;
api
:
SpringApi
;
options
?:
Options
;
})
=>
Promise
<
void
>
;
export
interface
MoveOptions
{
fromZone
?:
ygopro
.
CardZone
;
}
export
type
MoveFunc
=
OptionsToFunc
<
MoveOptions
>
;
export
type
AttackOptions
=
|
{
directAttack
:
true
;
}
|
{
directAttack
:
false
;
target
:
ygopro
.
CardLocation
};
export
type
AttackFunc
=
OptionsToFunc
<
AttackOptions
>
;
src/ui/Duel/PlayMat/Card/springs/utils.ts
View file @
a754c2c2
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
>
)
=>
{
return
(
p
:
Partial
<
T
>
&
{
config
?:
SpringConfig
})
=>
new
Promise
((
resolve
)
=>
{
...
...
@@ -14,9 +9,3 @@ export const asyncStart = <T extends {}>(api: SpringRef<T>) => {
});
});
};
export
type
MoveFunc
=
(
props
:
{
card
:
CardType
;
api
:
SpringApi
;
fromZone
?:
ygopro
.
CardZone
;
})
=>
Promise
<
void
>
;
src/ui/Duel/PlayMat/LifeBar/index.scss
View file @
a754c2c2
...
...
@@ -54,25 +54,3 @@
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 @
a754c2c2
...
...
@@ -115,7 +115,6 @@ const LifeBarItem: React.FC<{
size=
{
14
}
/>
<
div
className=
"timer-text"
>
{
timeText
}
</
div
>
<
div
className=
"floodlight floodlight-run"
/>
</
div
>
)
}
</
div
>
...
...
src/ui/Duel/PlayMat/utils/cssConfig.ts
deleted
100644 → 0
View file @
53a9bdf4
// type CSSValue = [number, string] | number;
export
type
CSSConfig
=
Record
<
string
,
{
value
:
number
;
unit
:
UNIT
}
>
;
/** 转为CSS变量: BOARD_ROTATE_Z -> --board-rotate-z */
export
const
toCssProperties
=
(
config
:
CSSConfig
)
=>
Object
.
entries
(
config
)
.
map
(
([
k
,
v
])
=>
[
`--
${
k
.
split
(
"
_
"
)
.
map
((
s
)
=>
s
.
toLowerCase
())
.
join
(
"
-
"
)}
`
,
`
${
v
.
value
}${
v
.
unit
}
`
,
]
as
[
string
,
string
]
)
.
reduce
((
acc
,
cur
)
=>
[...
acc
,
cur
],
[]
as
[
string
,
string
][]);
enum
UNIT
{
PX
=
"
px
"
,
DEG
=
"
deg
"
,
NONE
=
""
,
}
export
const
matConfig
=
{
PERSPECTIVE
:
{
value
:
1500
,
unit
:
UNIT
.
PX
,
},
PLANE_ROTATE_X
:
{
value
:
0
,
unit
:
UNIT
.
DEG
,
},
BLOCK_WIDTH
:
{
value
:
120
,
unit
:
UNIT
.
PX
,
},
BLOCK_HEIGHT_M
:
{
value
:
120
,
unit
:
UNIT
.
PX
,
},
// 主要怪兽区
BLOCK_HEIGHT_S
:
{
value
:
110
,
unit
:
UNIT
.
PX
,
},
// 魔法陷阱区
ROW_GAP
:
{
value
:
10
,
unit
:
UNIT
.
PX
,
},
COL_GAP
:
{
value
:
10
,
unit
:
UNIT
.
PX
,
},
CARD_RATIO
:
{
value
:
5.9
/
8.6
,
unit
:
UNIT
.
NONE
,
},
HAND_MARGIN_TOP
:
{
value
:
0
,
unit
:
UNIT
.
PX
,
},
HAND_CIRCLE_CENTER_OFFSET_Y
:
{
value
:
2000
,
unit
:
UNIT
.
PX
,
},
HAND_CARD_HEIGHT
:
{
value
:
130
,
unit
:
UNIT
.
PX
,
},
DECK_OFFSET_X
:
{
value
:
140
,
unit
:
UNIT
.
PX
,
},
DECK_OFFSET_Y
:
{
value
:
80
,
unit
:
UNIT
.
PX
,
},
DECK_ROTATE_Z
:
{
value
:
30
,
unit
:
UNIT
.
DEG
,
},
DECK_CARD_HEIGHT
:
{
value
:
120
,
unit
:
UNIT
.
PX
,
},
};
toCssProperties
(
matConfig
).
forEach
(([
k
,
v
])
=>
{
document
.
body
.
style
.
setProperty
(
k
,
v
);
});
src/ui/Duel/PlayMat/utils/index.ts
deleted
100644 → 0
View file @
53a9bdf4
export
*
from
"
./cssConfig
"
;
src/ui/Shared/YgoCard/index.tsx
View file @
a754c2c2
...
...
@@ -38,7 +38,7 @@ export const YgoCard: React.FC<Props> = (props) => {
const
NeosConfig
=
useConfig
();
function
getCardImgUrl
(
code
:
number
,
back
=
false
)
{
export
function
getCardImgUrl
(
code
:
number
,
back
=
false
)
{
const
ASSETS_BASE
=
import
.
meta
.
env
.
BASE_URL
===
"
/
"
?
NeosConfig
.
assetsPath
...
...
src/ui/Shared/css.ts
0 → 100644
View file @
a754c2c2
// 此文件目的是在js和CSS之间共享一些变量,并且这些变量是0运行时的。
type
CSSConfig
=
Record
<
string
,
[
number
,
UNIT
]
>
;
/** 转为CSS变量: BOARD_ROTATE_Z -> --board-rotate-z */
const
toCssProperties
=
(
config
:
CSSConfig
)
=>
Object
.
entries
(
config
)
.
map
(
([
k
,
v
])
=>
[
`--
${
k
.
split
(
"
_
"
)
.
map
((
s
)
=>
s
.
toLowerCase
())
.
join
(
"
-
"
)}
`
,
`
${
v
[
0
]}${
v
[
1
]}
`
,
]
as
[
string
,
string
]
)
.
reduce
((
acc
,
cur
)
=>
[...
acc
,
cur
],
[]
as
[
string
,
string
][]);
enum
UNIT
{
PX
=
"
px
"
,
DEG
=
"
deg
"
,
NONE
=
""
,
}
const
matConfigWithUnit
=
{
PERSPECTIVE
:
[
1500
,
UNIT
.
PX
],
PLANE_ROTATE_X
:
[
0
,
UNIT
.
DEG
],
BLOCK_WIDTH
:
[
120
,
UNIT
.
PX
],
BLOCK_HEIGHT_M
:
[
120
,
UNIT
.
PX
],
BLOCK_HEIGHT_S
:
[
110
,
UNIT
.
PX
],
// 魔法陷阱区
ROW_GAP
:
[
10
,
UNIT
.
PX
],
COL_GAP
:
[
10
,
UNIT
.
PX
],
CARD_RATIO
:
[
5.9
/
8.6
,
UNIT
.
NONE
],
HAND_MARGIN_TOP
:
[
0
,
UNIT
.
PX
],
HAND_CIRCLE_CENTER_OFFSET_Y
:
[
2000
,
UNIT
.
PX
],
HAND_CARD_HEIGHT
:
[
130
,
UNIT
.
PX
],
DECK_OFFSET_X
:
[
140
,
UNIT
.
PX
],
DECK_OFFSET_Y
:
[
80
,
UNIT
.
PX
],
DECK_ROTATE_Z
:
[
30
,
UNIT
.
DEG
],
DECK_CARD_HEIGHT
:
[
120
,
UNIT
.
PX
],
CARD_HEIGHT_O
:
[
100
,
UNIT
.
PX
],
// 场地魔法/墓地/除外的卡片高度
BLOCK_OUTSIDE_OFFSET_X
:
[
15
,
UNIT
.
PX
],
}
satisfies
CSSConfig
;
export
const
matConfig
=
Object
.
keys
(
matConfigWithUnit
).
reduce
(
(
prev
,
key
)
=>
({
...
prev
,
// @ts-ignore
[
key
]:
matConfigWithUnit
[
key
][
0
],
}),
{}
as
Record
<
keyof
typeof
matConfigWithUnit
,
number
>
);
toCssProperties
(
matConfigWithUnit
).
forEach
(([
k
,
v
])
=>
{
document
.
body
.
style
.
setProperty
(
k
,
v
);
});
src/ui/Shared/index.ts
View file @
a754c2c2
export
*
from
"
./css
"
;
export
*
from
"
./YgoCard
"
;
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