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
b4e4a4e8
Commit
b4e4a4e8
authored
Jun 22, 2023
by
Chunchi Che
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev/async_rebase' into 'dev/async'
Dev/async rebase See merge request
!234
parents
c78b6b62
d0b7a3c9
Pipeline
#22364
failed with stages
in 10 minutes and 22 seconds
Changes
22
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
1147 additions
and
48 deletions
+1147
-48
neos.config.json
neos.config.json
+1
-1
src/api/ocgcore/idl/ocgcore.ts
src/api/ocgcore/idl/ocgcore.ts
+762
-5
src/api/ocgcore/ocgAdapter/protoDecl.ts
src/api/ocgcore/ocgAdapter/protoDecl.ts
+3
-0
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/fieldDisabled.ts
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/fieldDisabled.ts
+73
-0
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/mod.ts
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/mod.ts
+13
-0
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/penetrate.json
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/penetrate.json
+9
-0
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/penetrate.ts
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/penetrate.ts
+1
-0
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/shuffleSetCard.ts
...api/ocgcore/ocgAdapter/stoc/stocGameMsg/shuffleSetCard.ts
+32
-0
src/service/duel/becomeTarget.ts
src/service/duel/becomeTarget.ts
+1
-1
src/service/duel/chainEnd.ts
src/service/duel/chainEnd.ts
+8
-0
src/service/duel/fieldDisabled.ts
src/service/duel/fieldDisabled.ts
+19
-0
src/service/duel/gameMsg.ts
src/service/duel/gameMsg.ts
+19
-1
src/service/duel/selectPlace.ts
src/service/duel/selectPlace.ts
+8
-5
src/service/duel/shuffleDeck.ts
src/service/duel/shuffleDeck.ts
+11
-0
src/service/duel/shuffleSetCard.ts
src/service/duel/shuffleSetCard.ts
+50
-0
src/service/duel/start.ts
src/service/duel/start.ts
+1
-0
src/service/duel/updateData.ts
src/service/duel/updateData.ts
+16
-11
src/stores/cardStore.ts
src/stores/cardStore.ts
+1
-2
src/stores/placeStore.ts
src/stores/placeStore.ts
+38
-9
src/ui/Duel/PlayMat/Bg/index.tsx
src/ui/Duel/PlayMat/Bg/index.tsx
+41
-11
src/ui/Duel/PlayMat/Card/index.scss
src/ui/Duel/PlayMat/Card/index.scss
+39
-2
src/ui/Duel/PlayMat/Card/index.tsx
src/ui/Duel/PlayMat/Card/index.tsx
+1
-0
No files found.
neos.config.json
View file @
b4e4a4e8
...
...
@@ -2,7 +2,7 @@
"version"
:
4960
,
"servers"
:[
{
"ip"
:
"koishi
-r
.momobako.com"
,
"ip"
:
"koishi.momobako.com"
,
"port"
:
"7211"
}
],
...
...
src/api/ocgcore/idl/ocgcore.ts
View file @
b4e4a4e8
This diff is collapsed.
Click to expand it.
src/api/ocgcore/ocgAdapter/protoDecl.ts
View file @
b4e4a4e8
...
...
@@ -35,6 +35,7 @@ export const MSG_NEW_PHASE = 41;
export
const
MSG_HINT
=
2
;
export
const
MSG_SELECT_IDLE_CMD
=
11
;
export
const
MSG_SELECT_PLACE
=
18
;
export
const
MSG_SELECT_DISFIELD
=
24
;
export
const
MSG_MOVE
=
50
;
export
const
MSG_SELECT_CARD
=
15
;
export
const
MSG_SELECT_TRIBUTE
=
20
;
...
...
@@ -63,3 +64,5 @@ export const MSG_ANNOUNCE_CARD = 142;
export
const
MSG_ANNOUNCE_NUMBER
=
143
;
export
const
MSG_TOSS_COIN
=
130
;
export
const
MSG_TOSS_DICE
=
131
;
export
const
MSG_SHUFFLE_SET_CARD
=
36
;
export
const
MSG_FIELD_DISABLED
=
56
;
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/fieldDisabled.ts
0 → 100644
View file @
b4e4a4e8
import
{
ygopro
}
from
"
@/api/ocgcore/idl/ocgcore
"
;
import
{
BufferReader
}
from
"
../../../../../../rust-src/pkg/rust_src
"
;
import
MsgFieldDisabled
=
ygopro
.
StocGameMessage
.
MsgFieldDisabled
;
import
CardZone
=
ygopro
.
CardZone
;
/*
* Msg Field Disabled
* @param - TODO
*
* @usage - 区域禁用
* */
export
default
(
data
:
Uint8Array
)
=>
{
const
reader
=
new
BufferReader
(
data
);
const
flag
=
reader
.
readInt32
();
const
actions
=
[];
let
filter
=
0x1
;
for
(
let
i
=
0
;
i
<
5
;
i
++
,
filter
<<=
1
)
{
const
disabled
=
(
flag
&
filter
)
>
0
;
actions
.
push
(
new
MsgFieldDisabled
.
Action
({
controller
:
0
,
zone
:
CardZone
.
MZONE
,
sequence
:
i
,
disabled
,
})
);
}
filter
=
0x100
;
for
(
let
i
=
0
;
i
<
8
;
i
++
,
filter
<<=
1
)
{
const
disabled
=
(
flag
&
filter
)
>
0
;
actions
.
push
(
new
MsgFieldDisabled
.
Action
({
controller
:
0
,
zone
:
CardZone
.
SZONE
,
sequence
:
i
,
disabled
,
})
);
}
filter
=
0x10000
;
for
(
let
i
=
0
;
i
<
5
;
i
++
,
filter
<<=
1
)
{
const
disabled
=
(
flag
&
filter
)
>
0
;
actions
.
push
(
new
MsgFieldDisabled
.
Action
({
controller
:
1
,
zone
:
CardZone
.
MZONE
,
sequence
:
i
,
disabled
,
})
);
}
filter
=
0x1000000
;
for
(
let
i
=
0
;
i
<
8
;
i
++
,
filter
<<=
1
)
{
const
disabled
=
(
flag
&
filter
)
>
0
;
actions
.
push
(
new
MsgFieldDisabled
.
Action
({
controller
:
1
,
zone
:
CardZone
.
SZONE
,
sequence
:
i
,
disabled
,
})
);
}
return
new
MsgFieldDisabled
({
actions
,
});
};
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/mod.ts
View file @
b4e4a4e8
...
...
@@ -14,6 +14,7 @@ import MsgAnnounceRace from "./announceRace";
import
MsgAttack
from
"
./attack
"
;
import
MsgDamage
from
"
./damage
"
;
import
MsgDrawAdapter
from
"
./draw
"
;
import
MsgFieldDisabledAdapter
from
"
./fieldDisabled
"
;
import
MsgHintAdapter
from
"
./hint
"
;
import
MsgNewPhaseAdapter
from
"
./newPhase
"
;
import
MsgNewTurnAdapter
from
"
./newTurn
"
;
...
...
@@ -33,6 +34,7 @@ import MsgSelectPositionAdapter from "./selectPosition";
import
MsgSelectSum
from
"
./selectSum
"
;
import
MsgSelectTributeAdapter
from
"
./selectTribute
"
;
import
MsgSelectUnselectCardAdapter
from
"
./selectUnselectCard
"
;
import
MsgShuffleSetCard
from
"
./shuffleSetCard
"
;
import
MsgSortCard
from
"
./sortCard
"
;
import
MsgStartAdapter
from
"
./start
"
;
import
MsgTossAdapter
from
"
./toss
"
;
...
...
@@ -95,6 +97,7 @@ export default class GameMsgAdapter implements StocAdapter {
break
;
}
case
GAME_MSG
.
MSG_SELECT_DISFIELD
:
case
GAME_MSG
.
MSG_SELECT_PLACE
:
{
gameMsg
.
select_place
=
MsgSelectPlaceAdapter
(
gameData
);
...
...
@@ -237,6 +240,16 @@ export default class GameMsgAdapter implements StocAdapter {
break
;
}
case
GAME_MSG
.
MSG_SHUFFLE_SET_CARD
:
{
gameMsg
.
shuffle_set_card
=
MsgShuffleSetCard
(
gameData
);
break
;
}
case
GAME_MSG
.
MSG_FIELD_DISABLED
:
{
gameMsg
.
field_disabled
=
MsgFieldDisabledAdapter
(
gameData
);
break
;
}
default
:
{
gameMsg
.
unimplemented
=
new
ygopro
.
StocGameMessage
.
MsgUnimplemented
({
command
:
func
,
...
...
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/penetrate.json
View file @
b4e4a4e8
...
...
@@ -235,5 +235,14 @@
"repeatedType"
:
"CardLocation"
}
]
},
"32"
:{
"protoType"
:
"shuffle_deck"
,
"fields"
:[
{
"fieldName"
:
"player"
,
"fieldType"
:
"uint8"
}
]
}
}
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/penetrate.ts
View file @
b4e4a4e8
...
...
@@ -35,6 +35,7 @@ const MsgConstructorMap: Map<string, Constructor> = new Map([
[
"
lp_update
"
,
ygopro
.
StocGameMessage
.
MsgLpUpdate
],
[
"
confirm_cards
"
,
ygopro
.
StocGameMessage
.
MsgConfirmCards
],
[
"
become_target
"
,
ygopro
.
StocGameMessage
.
MsgBecomeTarget
],
[
"
shuffle_deck
"
,
ygopro
.
StocGameMessage
.
MsgShuffleDeck
],
]);
export
interface
penetrateType
{
...
...
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/shuffleSetCard.ts
0 → 100644
View file @
b4e4a4e8
import
{
ygopro
}
from
"
@/api/ocgcore/idl/ocgcore
"
;
import
{
BufferReaderExt
}
from
"
../../bufferIO
"
;
import
{
numberToCardZone
}
from
"
../../util
"
;
import
MsgShuffleSetCard
=
ygopro
.
StocGameMessage
.
MsgShuffleSetCard
;
/*
* Msg Shuffle Set Card
* @param - TODO
*
* @usage - 盖卡切洗
* */
export
default
(
data
:
Uint8Array
)
=>
{
const
reader
=
new
BufferReaderExt
(
data
);
const
zone
=
numberToCardZone
(
reader
.
inner
.
readUint8
());
const
count
=
reader
.
inner
.
readUint8
();
const
from_locations
=
[];
const
overlay_locations
=
[];
// TODO: 这个字段是否有用?
for
(
let
i
=
0
;
i
<
count
;
i
++
)
{
from_locations
.
push
(
reader
.
readCardLocation
());
}
for
(
let
i
=
0
;
i
<
count
;
i
++
)
{
overlay_locations
.
push
(
reader
.
readCardLocation
());
}
return
new
MsgShuffleSetCard
({
zone
,
from_locations
,
overlay_locations
,
});
};
src/service/duel/becomeTarget.ts
View file @
b4e4a4e8
...
...
@@ -10,7 +10,7 @@ export default (becomeTarget: ygopro.StocGameMessage.MsgBecomeTarget) => {
);
if
(
target
)
{
console
.
info
(
`
${
target
.
meta
.
text
.
name
}
become target`
);
// TODO: 动画
target
.
selected
=
true
;
}
else
{
console
.
warn
(
`<BecomeTarget>target from
${
location
}
is null`
);
}
...
...
src/service/duel/chainEnd.ts
View file @
b4e4a4e8
...
...
@@ -15,4 +15,12 @@ export default (_chainEnd: ygopro.StocGameMessage.MsgChainEnd) => {
console
.
warn
(
`<ChainEnd>target from
${
chain
}
is null`
);
}
}
// 目前selected字段只会涉及连锁过程某些卡成为效果对象,
// 因此在连锁结束的时候把selected标记清掉。
//
// TODO: 这里每次都要全部遍历一遍,后续可以优化下
for
(
const
card
of
cardStore
.
inner
)
{
card
.
selected
=
false
;
}
};
src/service/duel/fieldDisabled.ts
0 → 100644
View file @
b4e4a4e8
import
{
ygopro
}
from
"
@/api
"
;
import
{
placeStore
}
from
"
@/stores
"
;
import
MsgFieldDisabled
=
ygopro
.
StocGameMessage
.
MsgFieldDisabled
;
export
default
(
fieldDisabled
:
MsgFieldDisabled
)
=>
{
for
(
const
action
of
fieldDisabled
.
actions
)
{
switch
(
action
.
zone
)
{
case
ygopro
.
CardZone
.
MZONE
:
case
ygopro
.
CardZone
.
SZONE
:
placeStore
.
set
(
action
.
zone
,
action
.
controller
,
action
.
sequence
,
{
interactivity
:
undefined
,
disabled
:
action
.
disabled
,
});
break
;
default
:
console
.
warn
(
"
<FieldDisabled>zone is not MZONE nor SZONE!
"
);
}
}
};
src/service/duel/gameMsg.ts
View file @
b4e4a4e8
...
...
@@ -12,6 +12,7 @@ import onMsgChaining from "./chaining";
import
onMsgChainSolved
from
"
./chainSolved
"
;
import
onConfirmCards
from
"
./confirmCards
"
;
import
onMsgDraw
from
"
./draw
"
;
import
onMsgFieldDisabled
from
"
./fieldDisabled
"
;
import
onMsgFilpSummoned
from
"
./flipSummoned
"
;
import
onMsgFlipSummoning
from
"
./flipSummoning
"
;
import
onMsgHint
from
"
./hint
"
;
...
...
@@ -35,7 +36,9 @@ import onMsgSelectTribute from "./selectTribute";
import
onMsgSelectUnselectCard
from
"
./selectUnselectCard
"
;
import
onMsgSelectYesNo
from
"
./selectYesNo
"
;
import
onMsgSet
from
"
./set
"
;
import
onMsgShuffleDeck
from
"
./shuffleDeck
"
;
import
onMsgShuffleHand
from
"
./shuffleHand
"
;
import
onMsgShuffleSetCard
from
"
./shuffleSetCard
"
;
import
onMsgSortCard
from
"
./sortCard
"
;
import
onMsgSpSummoned
from
"
./spSummoned
"
;
import
onMsgSpSummoning
from
"
./spSummoning
"
;
...
...
@@ -186,7 +189,7 @@ async function _handleGameMsg(pb: ygopro.YgoStocMsg) {
break
;
}
case
"
update_data
"
:
{
onMsgUpdateData
(
msg
.
update_data
);
await
onMsgUpdateData
(
msg
.
update_data
);
break
;
}
...
...
@@ -310,6 +313,21 @@ async function _handleGameMsg(pb: ygopro.YgoStocMsg) {
break
;
}
case
"
shuffle_set_card
"
:
{
await
onMsgShuffleSetCard
(
msg
.
shuffle_set_card
);
break
;
}
case
"
field_disabled
"
:
{
onMsgFieldDisabled
(
msg
.
field_disabled
);
break
;
}
case
"
shuffle_deck
"
:
{
onMsgShuffleDeck
(
msg
.
shuffle_deck
);
break
;
}
case
"
unimplemented
"
:
{
onUnimplemented
(
msg
.
unimplemented
);
...
...
src/service/duel/selectPlace.ts
View file @
b4e4a4e8
...
...
@@ -14,12 +14,15 @@ export default (selectPlace: MsgSelectPlace) => {
case
ygopro
.
CardZone
.
MZONE
:
case
ygopro
.
CardZone
.
SZONE
:
placeStore
.
set
(
place
.
zone
,
place
.
controller
,
place
.
sequence
,
{
interactType
:
InteractType
.
PLACE_SELECTABLE
,
response
:
{
controller
:
place
.
controller
,
zone
:
place
.
zone
,
sequence
:
place
.
sequence
,
interactivity
:
{
interactType
:
InteractType
.
PLACE_SELECTABLE
,
response
:
{
controller
:
place
.
controller
,
zone
:
place
.
zone
,
sequence
:
place
.
sequence
,
},
},
disabled
:
false
,
});
break
;
}
...
...
src/service/duel/shuffleDeck.ts
0 → 100644
View file @
b4e4a4e8
import
{
ygopro
}
from
"
@/api
"
;
import
{
cardStore
}
from
"
@/stores
"
;
export
default
(
shuffleDeck
:
ygopro
.
StocGameMessage
.
MsgShuffleDeck
)
=>
{
const
player
=
shuffleDeck
.
player
;
for
(
const
card
of
cardStore
.
at
(
ygopro
.
CardZone
.
DECK
,
player
))
{
// 把数据抹掉就好了
card
.
code
=
0
;
card
.
meta
=
{
id
:
0
,
data
:
{},
text
:
{}
};
}
};
src/service/duel/shuffleSetCard.ts
0 → 100644
View file @
b4e4a4e8
import
{
ygopro
}
from
"
@/api
"
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
}
from
"
@/stores
"
;
import
MsgShuffleSetCard
=
ygopro
.
StocGameMessage
.
MsgShuffleSetCard
;
// 后端传过来的`from_locations`的列表是切洗前场上卡的location,它们在列表里面按照切洗后的顺序排列
export
default
async
(
shuffleSetCard
:
MsgShuffleSetCard
)
=>
{
const
from_locations
=
shuffleSetCard
.
from_locations
;
const
overlay_locations
=
shuffleSetCard
.
overlay_locations
;
if
(
from_locations
.
length
==
0
)
{
console
.
error
(
"
<ShuffleSetCard>from_locations is empty
"
);
return
;
}
if
(
from_locations
.
length
!=
overlay_locations
.
length
)
{
console
.
error
(
"
<ShuffleSetCard>length of from_locations and overlay_locations not matched
"
);
}
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社没想好的设计?
}
}
}
};
src/service/duel/start.ts
View file @
b4e4a4e8
...
...
@@ -62,6 +62,7 @@ export default async (start: ygopro.StocGameMessage.MsgStart) => {
text
:
{},
},
isToken
:
!
((
i
+
1
)
%
3
),
selected
:
false
,
})
)
)
...
...
src/service/duel/updateData.ts
View file @
b4e4a4e8
import
{
ygopro
}
from
"
@/api
"
;
import
{
fetchCard
,
ygopro
}
from
"
@/api
"
;
import
MsgUpdateData
=
ygopro
.
StocGameMessage
.
MsgUpdateData
;
import
{
eventbus
,
Task
}
from
"
@/infra
"
;
import
{
cardStore
}
from
"
@/stores
"
;
export
default
(
updateData
:
MsgUpdateData
)
=>
{
export
default
async
(
updateData
:
MsgUpdateData
)
=>
{
const
{
player
:
controller
,
zone
,
actions
}
=
updateData
;
if
(
controller
!==
undefined
&&
zone
!==
undefined
&&
actions
!==
undefined
)
{
const
field
=
cardStore
.
at
(
zone
,
controller
);
actions
.
forEach
((
action
)
=>
{
for
(
const
action
of
actions
)
{
const
sequence
=
action
.
location
?.
sequence
;
if
(
typeof
sequence
!==
"
undefined
"
)
{
const
target
=
field
.
filter
((
card
)
=>
card
.
location
.
sequence
===
sequence
)
.
at
(
0
);
if
(
target
)
{
const
meta
=
target
.
meta
;
// 目前只更新以下字段
if
(
action
?.
code
>=
0
)
{
meta
.
id
=
action
.
code
;
meta
.
text
.
id
=
action
.
code
;
const
newMeta
=
await
fetchCard
(
action
.
code
);
target
.
code
=
action
.
code
;
target
.
meta
=
newMeta
;
}
const
meta
=
target
.
meta
;
if
(
action
.
location
!==
undefined
)
{
target
.
location
.
position
=
action
.
location
.
position
;
if
(
target
.
location
.
position
!=
action
.
location
.
position
)
{
// Currently only update position
target
.
location
.
position
=
action
.
location
.
position
;
// animation
await
eventbus
.
call
(
Task
.
Move
,
target
.
uuid
);
}
}
if
(
action
?.
type_
>=
0
)
{
meta
.
data
.
type
=
action
.
type_
;
...
...
@@ -48,10 +56,7 @@ export default (updateData: MsgUpdateData) => {
);
console
.
info
(
field
);
}
if
(
target
?.
reload
)
{
target
.
reload
=
false
;
}
}
}
);
}
}
};
src/stores/cardStore.ts
View file @
b4e4a4e8
...
...
@@ -20,11 +20,10 @@ export interface CardType {
sequence
:
number
;
}
>
;
// 选择位置状态下的互动信息
counters
:
{
[
type
:
number
]:
number
};
// 指示器
reload
?:
boolean
;
// 这个字段会在收到MSG_RELOAD_FIELD的时候设置成true,在收到MSG_UPDATE_DATE的时候设置成false
isToken
:
boolean
;
// 是否是token
chainIndex
?:
number
/*连锁的序号,如果为空表示不在连锁
TODO: 目前是妥协的设计,因为其实一张卡是可以在同一个连锁链中被连锁多次的,这里为了避免太过复杂只保存最后的连锁序号*/
;
selected
:
boolean
;
// 当前卡是否被选择成为效果的对象
}
class
CardStore
{
...
...
src/stores/placeStore.ts
View file @
b4e4a4e8
...
...
@@ -15,31 +15,60 @@ export type PlaceInteractivity =
const
{
MZONE
,
SZONE
}
=
ygopro
.
CardZone
;
export
interface
BlockState
{
interactivity
?:
PlaceInteractivity
;
// 互动性
disabled
:
boolean
;
// 是否被禁用
}
export
const
placeStore
=
proxy
({
inner
:
{
[
MZONE
]:
{
me
:
Array
.
from
({
length
:
7
}).
map
(()
=>
undefined
as
PlaceInteractivity
),
op
:
Array
.
from
({
length
:
7
}).
map
(()
=>
undefined
as
PlaceInteractivity
),
me
:
Array
.
from
({
length
:
7
}).
map
(
()
=>
({
interactivity
:
undefined
,
disabled
:
false
,
}
as
BlockState
)
),
op
:
Array
.
from
({
length
:
7
}).
map
(
()
=>
({
interactivity
:
undefined
,
disabled
:
false
,
}
as
BlockState
)
),
},
[
SZONE
]:
{
me
:
Array
.
from
({
length
:
6
}).
map
(()
=>
undefined
as
PlaceInteractivity
),
op
:
Array
.
from
({
length
:
6
}).
map
(()
=>
undefined
as
PlaceInteractivity
),
me
:
Array
.
from
({
length
:
6
}).
map
(
()
=>
({
interactivity
:
undefined
,
disabled
:
false
,
}
as
BlockState
)
),
op
:
Array
.
from
({
length
:
6
}).
map
(
()
=>
({
interactivity
:
undefined
,
disabled
:
false
,
}
as
BlockState
)
),
},
},
set
(
zone
:
ygopro
.
CardZone
.
MZONE
|
ygopro
.
CardZone
.
SZONE
,
controller
:
number
,
sequence
:
number
,
placeInteractivity
:
PlaceInteractivity
state
:
BlockState
)
{
placeStore
.
inner
[
zone
][
matStore
.
isMe
(
controller
)
?
"
me
"
:
"
op
"
][
sequence
]
=
placeInteractivity
;
state
;
},
clearAll
()
{
clearAll
Interactivity
()
{
([
"
me
"
,
"
op
"
]
as
const
).
forEach
((
who
)
=>
{
([
MZONE
,
SZONE
]
as
const
).
forEach
((
where
)
=>
{
placeStore
.
inner
[
where
][
who
]
=
placeStore
.
inner
[
where
][
who
].
map
(
(
)
=>
undefined
placeStore
.
inner
[
where
][
who
]
.
forEach
(
(
block
)
=>
(
block
.
interactivity
=
undefined
)
);
});
});
...
...
src/ui/Duel/PlayMat/Bg/index.tsx
View file @
b4e4a4e8
import
"
./index.scss
"
;
import
classnames
from
"
classnames
"
;
import
{
type
FC
}
from
"
react
"
;
import
{
type
CSSProperties
,
type
FC
}
from
"
react
"
;
import
{
type
INTERNAL_Snapshot
as
Snapshot
,
useSnapshot
}
from
"
valtio
"
;
import
{
sendSelectPlaceResponse
,
ygopro
}
from
"
@/api
"
;
import
{
cardStore
,
type
PlaceInteractivity
,
placeStore
}
from
"
@/stores
"
;
import
{
BlockState
,
cardStore
,
type
PlaceInteractivity
,
placeStore
,
}
from
"
@/stores
"
;
// Block被禁用的样式
const
BgDisabledStyle
=
{
background
:
`linear-gradient(
to top right,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0) calc(50% - 1.5px),
red 50%,
rgba(0, 0, 0, 0) calc(50% + 1.5px),
rgba(0, 0, 0, 0) 100%
), linear-gradient(
to bottom right,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0) calc(50% - 1.5px),
red 50%,
rgba(0, 0, 0, 0) calc(50% + 1.5px),
rgba(0, 0, 0, 0) 100%
)`
,
};
const
BgExtraRow
:
FC
<
{
meSnap
:
Snapshot
<
PlaceInteractivity
[]
>
;
opSnap
:
Snapshot
<
PlaceInteractivity
[]
>
;
meSnap
:
Snapshot
<
BlockState
[]
>
;
opSnap
:
Snapshot
<
BlockState
[]
>
;
}
>
=
({
meSnap
,
opSnap
})
=>
{
return
(
<
div
className=
{
classnames
(
"
bg-row
"
)
}
>
...
...
@@ -17,11 +41,16 @@ const BgExtraRow: FC<{
<
div
key=
{
i
}
className=
{
classnames
(
"
block
"
,
"
extra
"
,
{
highlight
:
!!
meSnap
[
i
]
||
!!
opSnap
[
i
]
,
highlight
:
!!
meSnap
[
i
]
.
interactivity
||
!!
opSnap
[
i
].
interactivity
,
})
}
style=
{
meSnap
[
i
].
disabled
||
opSnap
[
i
].
disabled
?
(
BgDisabledStyle
as
CSSProperties
)
:
{}
}
onClick=
{
()
=>
{
onBlockClick
(
meSnap
[
i
]);
onBlockClick
(
opSnap
[
i
]);
onBlockClick
(
meSnap
[
i
]
.
interactivity
);
onBlockClick
(
opSnap
[
i
]
.
interactivity
);
}
}
>
{
<
DecoTriangles
/>
}
...
...
@@ -34,7 +63,7 @@ const BgExtraRow: FC<{
const
BgRow
:
FC
<
{
isSzone
?:
boolean
;
opponent
?:
boolean
;
snap
:
Snapshot
<
PlaceInteractivity
[]
>
;
snap
:
Snapshot
<
BlockState
[]
>
;
}
>
=
({
isSzone
=
false
,
opponent
=
false
,
snap
})
=>
(
<
div
className=
{
classnames
(
"
bg-row
"
,
{
opponent
})
}
>
{
Array
.
from
({
length
:
5
}).
map
((
_
,
i
)
=>
(
...
...
@@ -42,9 +71,10 @@ const BgRow: FC<{
key=
{
i
}
className=
{
classnames
(
"
block
"
,
{
szone
:
isSzone
,
highlight
:
!!
snap
[
i
],
highlight
:
!!
snap
[
i
]
.
interactivity
,
})
}
onClick=
{
()
=>
onBlockClick
(
snap
[
i
])
}
style=
{
snap
[
i
].
disabled
?
(
BgDisabledStyle
as
CSSProperties
)
:
{}
}
onClick=
{
()
=>
onBlockClick
(
snap
[
i
].
interactivity
)
}
>
{
<
DecoTriangles
/>
}
</
div
>
...
...
@@ -72,7 +102,7 @@ const onBlockClick = (placeInteractivity: PlaceInteractivity) => {
if
(
placeInteractivity
)
{
sendSelectPlaceResponse
(
placeInteractivity
.
response
);
cardStore
.
inner
.
forEach
((
card
)
=>
(
card
.
idleInteractivities
=
[]));
placeStore
.
clearAll
();
placeStore
.
clearAll
Interactivity
();
}
};
...
...
src/ui/Duel/PlayMat/Card/index.scss
View file @
b4e4a4e8
...
...
@@ -9,8 +9,10 @@ section#mat {
.card-img-wrap
{
transform-style
:
preserve-3d
;
position
:
relative
;
height
:
100%
;
width
:
100%
;
margin
:
auto
auto
;
top
:
2%
;
height
:
96%
;
width
:
96%
;
transform
:
translateZ
(
calc
(
var
(
--
z
)
*
1px
+
0
.1px
))
rotateY
(
calc
(
var
(
--
ry
)
*
1deg
));
transition
:
0
.2s
scale
;
...
...
@@ -47,6 +49,41 @@ section#mat {
background-color
:
transparent
;
// filter: blur(2px);
}
// 卡片被选中后的流光特效
// ref: https://github.com/Mr-majifu/Animated-Profile-Card02/blob/master/style.css
.card-streamer
{
position
:
absolute
;
inset
:
0
;
background
:
#000
;
overflow
:
hidden
;
}
.
card-streamer
:
:
before
{
content
:
''
;
position
:
absolute
;
top
:
50%
;
left
:
50%
;
width
:
400%
;
height
:
80%
;
background
:
linear-gradient
(
transparent
,
#45f3ff
,
#45f3ff
,
#45f3ff
,
transparent
);
animation
:
stream
2s
linear
infinite
;
}
.
card-streamer
:
:
after
{
content
:
''
;
position
:
absolute
;
/* https://developer.mozilla.org/en-US/docs/Web/CSS/inset */
inset
:
3px
;
background
:
#292929
;
}
@keyframes
stream
{
0
%
{
transform
:
translate
(
-50%
,
-50%
)
rotate
(
0deg
);
}
100
%
{
transform
:
translate
(
-50%
,
-50%
)
rotate
(
360deg
);
}
}
.card-focus
{
position
:
absolute
;
width
:
calc
(
100%
*
var
(
--
focus-scale
));
...
...
src/ui/Duel/PlayMat/Card/index.tsx
View file @
b4e4a4e8
...
...
@@ -241,6 +241,7 @@ export const Card: FC<{ idx: number }> = React.memo(({ idx }) => {
<
YgoCard
className=
"card-back"
isBack
/>
</
div
>
</
Dropdown
>
{
snap
.
selected
?
<
div
className=
"card-streamer"
/>
:
<></>
}
</
animated
.
div
>
);
});
...
...
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