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
deft
Neos
Commits
db603083
Commit
db603083
authored
Apr 15, 2023
by
timel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: rename ignore case
parent
2ced21ca
Changes
27
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
2358 additions
and
0 deletions
+2358
-0
src/ui/Duel/Layout.tsx
src/ui/Duel/Layout.tsx
+59
-0
src/ui/Duel/Main.tsx
src/ui/Duel/Main.tsx
+126
-0
src/ui/Duel/Message/Alert.tsx
src/ui/Duel/Message/Alert.tsx
+35
-0
src/ui/Duel/Message/CardListModal.tsx
src/ui/Duel/Message/CardListModal.tsx
+66
-0
src/ui/Duel/Message/CardModal.tsx
src/ui/Duel/Message/CardModal.tsx
+164
-0
src/ui/Duel/Message/CheckCardModal.tsx
src/ui/Duel/Message/CheckCardModal.tsx
+169
-0
src/ui/Duel/Message/CheckCardModalV2.tsx
src/ui/Duel/Message/CheckCardModalV2.tsx
+127
-0
src/ui/Duel/Message/CheckCardModalV3.tsx
src/ui/Duel/Message/CheckCardModalV3.tsx
+123
-0
src/ui/Duel/Message/CheckCounterModal.tsx
src/ui/Duel/Message/CheckCounterModal.tsx
+73
-0
src/ui/Duel/Message/DragModal.tsx
src/ui/Duel/Message/DragModal.tsx
+45
-0
src/ui/Duel/Message/HintNotification.tsx
src/ui/Duel/Message/HintNotification.tsx
+71
-0
src/ui/Duel/Message/OptionModal.tsx
src/ui/Duel/Message/OptionModal.tsx
+56
-0
src/ui/Duel/Message/Phase.tsx
src/ui/Duel/Message/Phase.tsx
+145
-0
src/ui/Duel/Message/PositionModal.tsx
src/ui/Duel/Message/PositionModal.tsx
+86
-0
src/ui/Duel/Message/SendBox.tsx
src/ui/Duel/Message/SendBox.tsx
+36
-0
src/ui/Duel/Message/SortCardModal.tsx
src/ui/Duel/Message/SortCardModal.tsx
+115
-0
src/ui/Duel/Message/Status.tsx
src/ui/Duel/Message/Status.tsx
+65
-0
src/ui/Duel/Message/TimeLine.tsx
src/ui/Duel/Message/TimeLine.tsx
+26
-0
src/ui/Duel/Message/YesNoModal.tsx
src/ui/Duel/Message/YesNoModal.tsx
+50
-0
src/ui/Duel/PlayMat/Deck.tsx
src/ui/Duel/PlayMat/Deck.tsx
+36
-0
src/ui/Duel/PlayMat/ExtraDeck.tsx
src/ui/Duel/PlayMat/ExtraDeck.tsx
+39
-0
src/ui/Duel/PlayMat/Field.tsx
src/ui/Duel/PlayMat/Field.tsx
+54
-0
src/ui/Duel/PlayMat/FixedSlot.tsx
src/ui/Duel/PlayMat/FixedSlot.tsx
+132
-0
src/ui/Duel/PlayMat/Hands.tsx
src/ui/Duel/PlayMat/Hands.tsx
+172
-0
src/ui/Duel/PlayMat/Magics.tsx
src/ui/Duel/PlayMat/Magics.tsx
+64
-0
src/ui/Duel/PlayMat/Monsters.tsx
src/ui/Duel/PlayMat/Monsters.tsx
+137
-0
src/ui/Duel/PlayMat/SingleSlot.tsx
src/ui/Duel/PlayMat/SingleSlot.tsx
+87
-0
No files found.
src/ui/Duel/Layout.tsx
0 → 100644
View file @
db603083
import
React
from
"
react
"
;
import
{
Layout
}
from
"
antd
"
;
import
NeosConfig
from
"
../../../neos.config.json
"
;
const
layoutConfig
=
NeosConfig
.
ui
.
layout
;
const
{
Header
,
Footer
,
Sider
,
Content
}
=
Layout
;
const
headerStyle
:
React
.
CSSProperties
=
{
textAlign
:
"
center
"
,
alignContent
:
"
center
"
,
color
:
"
#fff
"
,
height
:
layoutConfig
.
header
.
height
,
};
const
contentStyle
:
React
.
CSSProperties
=
{
textAlign
:
"
center
"
,
minHeight
:
120
,
height
:
layoutConfig
.
content
.
height
,
lineHeight
:
"
120px
"
,
paddingLeft
:
`
${
layoutConfig
.
sider
.
width
}
px`
,
};
const
siderStyle
:
React
.
CSSProperties
=
{
lineHeight
:
"
120px
"
,
position
:
"
fixed
"
,
overflow
:
"
auto
"
,
height
:
"
100vh
"
,
padding
:
"
50px 20px
"
,
color
:
"
#fff
"
,
};
const
footerStyle
:
React
.
CSSProperties
=
{
textAlign
:
"
center
"
,
height
:
layoutConfig
.
footer
.
height
,
color
:
"
#fff
"
,
paddingLeft
:
`
${
layoutConfig
.
sider
.
width
}
px`
,
};
const
NeosLayout
=
(
props
:
{
sider
:
React
.
ReactNode
;
header
:
React
.
ReactNode
;
content
:
React
.
ReactNode
;
footer
:
React
.
ReactNode
;
})
=>
{
return
(
<
Layout
hasSider
>
<
Sider
style=
{
siderStyle
}
width=
{
layoutConfig
.
sider
.
width
}
>
{
props
.
sider
}
</
Sider
>
<
Layout
>
<
Header
style=
{
headerStyle
}
>
{
props
.
header
}
</
Header
>
<
Content
style=
{
contentStyle
}
>
{
props
.
content
}
</
Content
>
<
Footer
style=
{
footerStyle
}
>
{
props
.
footer
}
</
Footer
>
</
Layout
>
</
Layout
>
);
};
export
default
NeosLayout
;
src/ui/Duel/Main.tsx
0 → 100644
View file @
db603083
import
React
from
"
react
"
;
import
{
Engine
,
Scene
}
from
"
react-babylonjs
"
;
import
{
ReactReduxContext
,
Provider
}
from
"
react-redux
"
;
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
Hands
from
"
./PlayMat/Hands
"
;
import
Monsters
from
"
./PlayMat/Monsters
"
;
import
CardModal
from
"
./Message/CardModal
"
;
import
HintNotification
from
"
./Message/HintNotification
"
;
import
Magics
from
"
./PlayMat/Magics
"
;
import
Field
from
"
./PlayMat/Field
"
;
import
CommonDeck
from
"
./PlayMat/Deck
"
;
import
Exclusion
from
"
./PlayMat/BanishedZone
"
;
import
Graveyard
from
"
./PlayMat/Graveyard
"
;
import
CardListModal
from
"
./Message/CardListModal
"
;
import
CheckCardModal
from
"
./Message/CheckCardModal
"
;
import
YesNoModal
from
"
./Message/YesNoModal
"
;
import
PositionModal
from
"
./Message/PositionModal
"
;
import
OptionModal
from
"
./Message/OptionModal
"
;
import
Phase
from
"
./Message/Phase
"
;
import
CheckCardModalV2
from
"
./Message/CheckCardModalV2
"
;
import
ExtraDeck
from
"
./PlayMat/ExtraDeck
"
;
import
NeosLayout
from
"
./Layout
"
;
import
NeosConfig
from
"
../../../neos.config.json
"
;
import
DuelTimeLine
from
"
./Message/TimeLine
"
;
import
{
Row
}
from
"
antd
"
;
import
SendBox
from
"
./Message/SendBox
"
;
import
PlayerStatus
from
"
./Message/Status
"
;
import
Alert
from
"
./Message/Alert
"
;
import
CheckCardModalV3
from
"
./Message/CheckCardModalV3
"
;
import
CheckCounterModal
from
"
./Message/CheckCounterModal
"
;
import
SortCardModal
from
"
./Message/SortCardModal
"
;
// Ref: https://github.com/brianzinn/react-babylonjs/issues/126
const
NeosDuel
=
()
=>
{
return
(
<>
<
Alert
/>
<
NeosLayout
sider=
{
<
NeosSider
/>
}
header=
{
<
PlayerStatus
/>
}
content=
{
<
NeosCanvas
/>
}
footer=
{
<
Phase
/>
}
/>
<
CardModal
/>
<
CardListModal
/>
<
HintNotification
/>
<
CheckCardModal
/>
<
YesNoModal
/>
<
PositionModal
/>
<
OptionModal
/>
<
CheckCardModalV2
/>
<
CheckCardModalV3
/>
<
CheckCounterModal
/>
<
SortCardModal
/>
</>
);
};
const
NeosSider
=
()
=>
(
<>
<
Row
>
<
DuelTimeLine
/>
</
Row
>
<
Row
>
<
SendBox
/>
</
Row
>
</>
);
const
NeosCanvas
=
()
=>
(
<
ReactReduxContext
.
Consumer
>
{
({
store
})
=>
(
<
Engine
antialias
adaptToDeviceRatio
canvasId=
"babylonJS"
>
<
Scene
>
<
Provider
store=
{
store
}
>
<
Camera
/>
<
Light
/>
<
Hands
/>
<
Monsters
/>
<
Magics
/>
<
Field
/>
<
CommonDeck
/>
<
ExtraDeck
/>
<
Graveyard
/>
<
Exclusion
/>
<
Field
/>
<
Ground
/>
</
Provider
>
</
Scene
>
</
Engine
>
)
}
</
ReactReduxContext
.
Consumer
>
);
const
Camera
=
()
=>
(
<
freeCamera
name=
"duel-camera"
position=
{
new
BABYLON
.
Vector3
(
0
,
8
,
-
10
)
}
target=
{
BABYLON
.
Vector3
.
Zero
()
}
></
freeCamera
>
);
const
Light
=
()
=>
(
<
hemisphericLight
name=
"duel-light"
direction=
{
new
BABYLON
.
Vector3
(
1
,
2.5
,
1
)
}
intensity=
{
0.7
}
></
hemisphericLight
>
);
const
Ground
=
()
=>
{
const
shape
=
NeosConfig
.
ui
.
ground
;
const
texture
=
new
BABYLON
.
Texture
(
`
${
NeosConfig
.
assetsPath
}
/newfield.png`
);
texture
.
hasAlpha
=
true
;
return
(
<
ground
name=
"duel-ground"
width=
{
shape
.
width
}
height=
{
shape
.
height
}
>
<
standardMaterial
name=
"duel-ground-mat"
diffuseTexture=
{
texture
}
></
standardMaterial
>
</
ground
>
);
};
export
default
NeosDuel
;
src/ui/Duel/Message/Alert.tsx
0 → 100644
View file @
db603083
import
React
from
"
react
"
;
import
{
useNavigate
}
from
"
react-router-dom
"
;
import
{
sendSurrender
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
selectUnimplemented
}
from
"
@/reducers/duel/mod
"
;
import
{
Alert
}
from
"
antd
"
;
const
NeosAlert
=
()
=>
{
const
unimplemented
=
useAppSelector
(
selectUnimplemented
);
const
navigate
=
useNavigate
();
return
(
<>
{
unimplemented
?
(
<
Alert
message=
{
`Unimplemented message with code=${unimplemented}`
}
description=
"It seems that there's something unimplemented by Neos. Sincerely apologize for that. Contact use to fix this issue: <ccc@neos.moe>"
showIcon
type=
"error"
closable
banner
afterClose=
{
()
=>
{
// 发送投降信号
sendSurrender
();
navigate
(
"
/
"
);
}
}
/>
)
:
(
<></>
)
}
</>
);
};
export
default
NeosAlert
;
src/ui/Duel/Message/CardListModal.tsx
0 → 100644
View file @
db603083
import
React
from
"
react
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
store
}
from
"
@/store
"
;
import
{
selectCardListModalIsOpen
,
selectCardListModalInfo
,
}
from
"
@/reducers/duel/modal/mod
"
;
import
{
clearAllIdleInteractivities
,
setCardListModalIsOpen
,
}
from
"
@/reducers/duel/mod
"
;
import
{
Drawer
,
List
,
Button
}
from
"
antd
"
;
import
{
sendSelectIdleCmdResponse
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
const
CARD_WIDTH
=
100
;
const
CardListModal
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
isOpen
=
useAppSelector
(
selectCardListModalIsOpen
);
const
list
=
useAppSelector
(
selectCardListModalInfo
);
const
handleOkOrCancel
=
()
=>
{
dispatch
(
setCardListModalIsOpen
(
false
));
};
return
(
<
Drawer
open=
{
isOpen
}
onClose=
{
handleOkOrCancel
}
>
<
List
itemLayout=
"horizontal"
dataSource=
{
list
}
renderItem=
{
(
item
)
=>
(
<
List
.
Item
actions=
{
item
.
interactivies
.
map
((
interactivy
,
idx
)
=>
(
<
Button
key=
{
idx
}
onClick=
{
()
=>
{
sendSelectIdleCmdResponse
(
interactivy
.
response
);
dispatch
(
setCardListModalIsOpen
(
false
));
dispatch
(
clearAllIdleInteractivities
(
0
));
dispatch
(
clearAllIdleInteractivities
(
1
));
}
}
>
{
interactivy
.
desc
}
</
Button
>
))
}
extra=
{
<
img
alt=
{
item
.
meta
?.
text
.
name
}
src=
{
`${NeosConfig.cardImgUrl}/${item.meta?.id}.jpg`
}
style=
{
{
width
:
CARD_WIDTH
}
}
/>
}
>
<
List
.
Item
.
Meta
title=
{
item
.
meta
?.
text
.
name
}
description=
{
item
.
meta
?.
text
.
desc
}
/>
</
List
.
Item
>
)
}
></
List
>
</
Drawer
>
);
};
export
default
CardListModal
;
src/ui/Duel/Message/CardModal.tsx
0 → 100644
View file @
db603083
import
React
from
"
react
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
store
}
from
"
@/store
"
;
import
{
selectCardModalIsOpen
,
selectCardModalInteractivies
,
selectCardModalMeta
,
selectCardModalCounters
,
}
from
"
@/reducers/duel/modal/mod
"
;
import
{
setCardModalIsOpen
,
clearAllIdleInteractivities
,
}
from
"
@/reducers/duel/mod
"
;
import
{
Modal
,
Card
,
Button
,
Row
,
Col
}
from
"
antd
"
;
import
{
sendSelectIdleCmdResponse
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
Icon
,
{
StarOutlined
}
from
"
@ant-design/icons
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
import
{
ReactComponent
as
BattleSvg
}
from
"
../../../../neos-assets/battle-axe.svg
"
;
import
{
ReactComponent
as
DefenceSvg
}
from
"
../../../../neos-assets/checked-shield.svg
"
;
import
{
extraCardTypes
,
Type2StringCodeMap
,
Attribute2StringCodeMap
,
Race2StringCodeMap
,
}
from
"
../../../common
"
;
import
{
fetchStrings
}
from
"
@/api/strings
"
;
const
{
Meta
}
=
Card
;
const
CARD_WIDTH
=
240
;
const
CardModal
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
isOpen
=
useAppSelector
(
selectCardModalIsOpen
);
const
meta
=
useAppSelector
(
selectCardModalMeta
);
const
name
=
meta
?.
text
.
name
;
const
types
=
meta
?.
data
.
type
;
const
race
=
meta
?.
data
.
race
;
const
attribute
=
meta
?.
data
.
attribute
;
const
level
=
meta
?.
data
.
level
;
const
desc
=
meta
?.
text
.
desc
;
const
atk
=
meta
?.
data
.
atk
;
const
def
=
meta
?.
data
.
def
;
const
counters
=
useAppSelector
(
selectCardModalCounters
);
const
imgUrl
=
meta
?.
id
?
`
${
NeosConfig
.
cardImgUrl
}
/
${
meta
.
id
}
.jpg`
:
undefined
;
const
interactivies
=
useAppSelector
(
selectCardModalInteractivies
);
const
handleOkOrCancel
=
()
=>
{
dispatch
(
setCardModalIsOpen
(
false
));
};
return
(
<
Modal
open=
{
isOpen
}
onOk=
{
handleOkOrCancel
}
onCancel=
{
handleOkOrCancel
}
>
<
Card
hoverable
style=
{
{
width
:
CARD_WIDTH
}
}
cover=
{
<
img
alt=
{
name
}
src=
{
imgUrl
}
/>
}
>
<
Meta
title=
{
name
}
/>
<
AttLine
types=
{
extraCardTypes
(
types
||
0
)
}
race=
{
race
}
attribute=
{
attribute
}
/>
<
AtkLine
level=
{
level
}
atk=
{
atk
}
def=
{
def
}
/>
<
CounterLine
counters=
{
counters
}
/>
<
p
>
{
desc
}
</
p
>
</
Card
>
{
interactivies
.
map
((
interactive
,
idx
)
=>
{
return
(
<
Button
key=
{
idx
}
onClick=
{
()
=>
{
sendSelectIdleCmdResponse
(
interactive
.
response
);
dispatch
(
setCardModalIsOpen
(
false
));
dispatch
(
clearAllIdleInteractivities
(
0
));
dispatch
(
clearAllIdleInteractivities
(
1
));
}
}
>
{
interactive
.
desc
}
</
Button
>
);
})
}
</
Modal
>
);
};
const
AtkLine
=
(
props
:
{
level
?:
number
;
atk
?:
number
;
def
?:
number
})
=>
(
<
Row
gutter=
{
8
}
>
{
props
.
level
?
(
<
Col
>
<
StarOutlined
/>
{
props
.
level
}
</
Col
>
)
:
(
<></>
)
}
{
props
.
atk
?
(
<
Col
>
<
Icon
component=
{
BattleSvg
}
/>
<
a
>
{
props
.
atk
}
</
a
>
</
Col
>
)
:
(
<></>
)
}
<
Col
>
/
</
Col
>
{
props
.
def
?
(
<
Col
>
<
Icon
component=
{
DefenceSvg
}
/>
<
a
>
{
props
.
def
}
</
a
>
</
Col
>
)
:
(
<></>
)
}
</
Row
>
);
const
AttLine
=
(
props
:
{
types
:
number
[];
race
?:
number
;
attribute
?:
number
;
})
=>
{
const
race
=
props
.
race
?
fetchStrings
(
"
!system
"
,
Race2StringCodeMap
.
get
(
props
.
race
)
||
0
)
:
undefined
;
const
attribute
=
props
.
attribute
?
fetchStrings
(
"
!system
"
,
Attribute2StringCodeMap
.
get
(
props
.
attribute
)
||
0
)
:
undefined
;
const
types
=
props
.
types
.
map
((
t
)
=>
fetchStrings
(
"
!system
"
,
Type2StringCodeMap
.
get
(
t
)
||
0
))
.
join
(
"
|
"
);
return
(
<
Row
gutter=
{
8
}
>
<
Col
>
{
`[${types}]`
}
</
Col
>
{
race
?
<
Col
>
{
race
}
</
Col
>
:
<></>
}
<
Col
>
/
</
Col
>
{
attribute
?
<
Col
>
{
attribute
}
</
Col
>
:
<></>
}
</
Row
>
);
};
const
CounterLine
=
(
props
:
{
counters
:
{
[
type
:
number
]:
number
}
})
=>
{
const
counters
=
[];
for
(
const
counterType
in
props
.
counters
)
{
const
count
=
props
.
counters
[
counterType
];
if
(
count
>
0
)
{
const
counterStr
=
fetchStrings
(
"
!counter
"
,
`0x
${
counterType
}
`
);
counters
.
push
(
`
${
counterStr
}
:
${
count
}
`
);
}
}
return
counters
.
length
>
0
?
(
<
Row
gutter=
{
8
}
>
{
counters
.
map
((
counter
)
=>
(
<
Col
>
{
counter
}
</
Col
>
))
}
</
Row
>
)
:
(
<></>
);
};
export
default
CardModal
;
src/ui/Duel/Message/CheckCardModal.tsx
0 → 100644
View file @
db603083
import
React
,
{
useState
}
from
"
react
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
store
}
from
"
@/store
"
;
import
{
selectCheckCardModalCacnelResponse
,
selectCheckCardModalCancelAble
,
selectCheckCardModalIsOpen
,
selectCheckCardModalMinMax
,
selectCheckCardModalOnSubmit
,
selectCheckCardModalTags
,
}
from
"
@/reducers/duel/modal/mod
"
;
import
{
resetCheckCardModal
,
setCheckCardModalIsOpen
,
}
from
"
@/reducers/duel/mod
"
;
import
{
Button
,
Row
,
Col
,
Popover
}
from
"
antd
"
;
import
{
CheckCard
,
CheckCardProps
}
from
"
@ant-design/pro-components
"
;
import
{
sendSelectCardResponse
,
sendSelectChainResponse
,
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
ThunderboltOutlined
}
from
"
@ant-design/icons
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
import
DragModal
from
"
./DragModal
"
;
import
{
selectHint
}
from
"
@/reducers/duel/hintSlice
"
;
const
CheckCardModal
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
isOpen
=
useAppSelector
(
selectCheckCardModalIsOpen
);
const
{
min
,
max
}
=
useAppSelector
(
selectCheckCardModalMinMax
);
const
tabs
=
useAppSelector
(
selectCheckCardModalTags
);
const
onSubmit
=
useAppSelector
(
selectCheckCardModalOnSubmit
);
const
cancelAble
=
useAppSelector
(
selectCheckCardModalCancelAble
);
const
cancelResponse
=
useAppSelector
(
selectCheckCardModalCacnelResponse
);
const
[
response
,
setResponse
]
=
useState
<
number
[]
>
([]);
const
defaultValue
:
number
[]
=
[];
const
hint
=
useAppSelector
(
selectHint
);
const
preHintMsg
=
hint
?.
esHint
||
""
;
const
selectHintMsg
=
hint
?.
esSelectHint
||
"
请选择卡片
"
;
// TODO: 这里可以考虑更好地封装
const
sendResponseHandler
=
(
handlerName
:
string
|
undefined
,
response
:
number
[]
)
=>
{
switch
(
handlerName
)
{
case
"
sendSelectChainResponse
"
:
{
sendSelectChainResponse
(
response
[
0
]);
break
;
}
case
"
sendSelectCardResponse
"
:
{
sendSelectCardResponse
(
response
);
break
;
}
default
:
{
}
}
};
return
(
<
DragModal
title=
{
`${preHintMsg} ${selectHintMsg} ${min}-${max}`
}
open=
{
isOpen
}
closable=
{
false
}
footer=
{
<>
<
Button
disabled=
{
response
.
length
<
min
||
response
.
length
>
max
}
onClick=
{
()
=>
{
sendResponseHandler
(
onSubmit
,
response
);
dispatch
(
setCheckCardModalIsOpen
(
false
));
dispatch
(
resetCheckCardModal
());
}
}
onFocus=
{
()
=>
{}
}
onBlur=
{
()
=>
{}
}
>
submit
</
Button
>
{
cancelAble
?
(
<
Button
onClick=
{
()
=>
{
if
(
cancelResponse
)
{
sendResponseHandler
(
onSubmit
,
[
cancelResponse
]);
}
dispatch
(
setCheckCardModalIsOpen
(
false
));
dispatch
(
resetCheckCardModal
());
}
}
onFocus=
{
()
=>
{}
}
onBlur=
{
()
=>
{}
}
>
cancel
</
Button
>
)
:
(
<></>
)
}
</>
}
width=
{
800
}
>
<
CheckCard
.
Group
multiple
bordered
size=
"small"
defaultValue=
{
defaultValue
}
onChange=
{
(
value
)
=>
{
// @ts-ignore
setResponse
(
value
);
}
}
>
{
tabs
.
map
((
tab
,
idx
)
=>
{
return
(
<
Row
key=
{
idx
}
>
{
tab
.
options
.
map
((
option
,
idx
)
=>
{
return
(
<
Col
span=
{
4
}
key=
{
idx
}
>
<
HoverCheckCard
hoverContent=
{
option
.
effectDesc
}
title=
{
option
.
meta
.
text
.
name
}
description=
{
option
.
meta
.
text
.
desc
}
style=
{
{
width
:
120
}
}
cover=
{
<
img
alt=
{
option
.
meta
.
id
.
toString
()
}
src=
{
option
.
meta
.
id
?
`${NeosConfig.cardImgUrl}/${option.meta.id}.jpg`
:
`${NeosConfig.assetsPath}/card_back.jpg`
}
style=
{
{
width
:
100
}
}
/>
}
value=
{
option
.
response
}
/>
</
Col
>
);
})
}
</
Row
>
);
})
}
</
CheckCard
.
Group
>
</
DragModal
>
);
};
const
HoverCheckCard
=
(
props
:
CheckCardProps
&
{
hoverContent
?:
string
})
=>
{
const
[
hover
,
setHover
]
=
useState
(
false
);
const
onMouseEnter
=
()
=>
setHover
(
true
);
const
onMouseLeave
=
()
=>
setHover
(
false
);
return
(
<>
<
CheckCard
{
...
props
}
/>
{
props
.
hoverContent
?
(
<
Popover
content=
{
<
p
>
{
props
.
hoverContent
}
</
p
>
}
open=
{
hover
}
>
<
Button
icon=
{
<
ThunderboltOutlined
/>
}
onMouseEnter=
{
onMouseEnter
}
onMouseLeave=
{
onMouseLeave
}
></
Button
>
</
Popover
>
)
:
(
<></>
)
}
</>
);
};
export
default
CheckCardModal
;
src/ui/Duel/Message/CheckCardModalV2.tsx
0 → 100644
View file @
db603083
import
React
from
"
react
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
store
}
from
"
@/store
"
;
import
{
Button
,
Card
,
Row
,
Col
}
from
"
antd
"
;
import
{
CheckCard
}
from
"
@ant-design/pro-components
"
;
import
{
selectCheckCardModalV2CancelAble
,
selectCheckCardModalV2FinishAble
,
selectCheckCardModalV2IsOpen
,
selectCheckCardModalV2MinMax
,
selectCheckCardModalV2ResponseAble
,
selectCheckCardModalV2SelectAbleOptions
,
selectCheckCardModalV2SelectedOptions
,
}
from
"
@/reducers/duel/modal/checkCardModalV2Slice
"
;
import
{
sendSelectUnselectCardResponse
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
resetCheckCardModalV2
,
setCheckCardModalV2IsOpen
,
setCheckCardModalV2ResponseAble
,
}
from
"
@/reducers/duel/mod
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
import
DragModal
from
"
./DragModal
"
;
import
{
selectHint
}
from
"
@/reducers/duel/hintSlice
"
;
const
CheckCardModalV2
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
isOpen
=
useAppSelector
(
selectCheckCardModalV2IsOpen
);
const
{
min
,
max
}
=
useAppSelector
(
selectCheckCardModalV2MinMax
);
const
cancelable
=
useAppSelector
(
selectCheckCardModalV2CancelAble
);
const
finishable
=
useAppSelector
(
selectCheckCardModalV2FinishAble
);
const
selectableOptions
=
useAppSelector
(
selectCheckCardModalV2SelectAbleOptions
);
const
selectedOptions
=
useAppSelector
(
selectCheckCardModalV2SelectedOptions
);
const
responseable
=
useAppSelector
(
selectCheckCardModalV2ResponseAble
);
const
hint
=
useAppSelector
(
selectHint
);
const
preHintMsg
=
hint
?.
esHint
||
""
;
const
selectHintMsg
=
hint
?.
esSelectHint
||
"
请选择卡片
"
;
const
onFinishOrCancel
=
()
=>
{
sendSelectUnselectCardResponse
({
cancel_or_finish
:
true
});
dispatch
(
setCheckCardModalV2IsOpen
(
false
));
dispatch
(
resetCheckCardModalV2
());
dispatch
(
setCheckCardModalV2ResponseAble
(
false
));
};
return
(
<
DragModal
title=
{
`${preHintMsg} ${selectHintMsg} ${min}-${max}`
}
open=
{
isOpen
}
closable=
{
false
}
footer=
{
<>
<
Button
disabled=
{
!
finishable
||
!
responseable
}
onClick=
{
onFinishOrCancel
}
>
finish
</
Button
>
<
Button
disabled=
{
!
cancelable
||
!
responseable
}
onClick=
{
onFinishOrCancel
}
>
cancel
</
Button
>
</>
}
width=
{
800
}
>
<
CheckCard
.
Group
bordered
size=
"small"
onChange=
{
(
value
)
=>
{
if
(
responseable
)
{
dispatch
(
setCheckCardModalV2IsOpen
(
false
));
// @ts-ignore
sendSelectUnselectCardResponse
({
selected_ptr
:
value
});
dispatch
(
setCheckCardModalV2ResponseAble
(
false
));
}
}
}
>
<
Row
>
{
selectableOptions
.
map
((
option
,
idx
)
=>
{
return
(
<
Col
span=
{
4
}
key=
{
idx
}
>
<
CheckCard
title=
{
option
.
name
}
description=
{
option
.
desc
}
style=
{
{
width
:
120
}
}
cover=
{
<
img
alt=
{
option
.
code
.
toString
()
}
src=
{
`${NeosConfig.cardImgUrl}/${option.code}.jpg`
}
style=
{
{
width
:
100
}
}
/>
}
value=
{
option
.
response
}
/>
</
Col
>
);
})
}
</
Row
>
</
CheckCard
.
Group
>
<
p
>
已经选择的卡片
</
p
>
<
Row
>
{
selectedOptions
.
map
((
option
,
idx
)
=>
{
return
(
<
Col
span=
{
4
}
key=
{
idx
}
>
<
Card
hoverable
style=
{
{
width
:
120
}
}
cover=
{
<
img
alt=
{
option
.
code
.
toString
()
}
src=
{
`${NeosConfig.cardImgUrl}/${option.code}.jpg`
}
/>
}
/>
</
Col
>
);
})
}
</
Row
>
</
DragModal
>
);
};
export
default
CheckCardModalV2
;
src/ui/Duel/Message/CheckCardModalV3.tsx
0 → 100644
View file @
db603083
import
React
,
{
useState
}
from
"
react
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
store
}
from
"
@/store
"
;
import
{
Button
,
Card
,
Row
,
Col
}
from
"
antd
"
;
import
{
CheckCard
}
from
"
@ant-design/pro-components
"
;
import
{
sendSelectCardResponse
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
resetCheckCardModalV3
,
setCheckCardModalV3IsOpen
,
setCheckCardModalV3ResponseAble
,
}
from
"
@/reducers/duel/mod
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
import
{
selectCheckCardModalV3
}
from
"
@/reducers/duel/modal/checkCardModalV3Slice
"
;
import
DragModal
from
"
./DragModal
"
;
import
{
selectHint
}
from
"
@/reducers/duel/hintSlice
"
;
const
CheckCardModalV3
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
state
=
useAppSelector
(
selectCheckCardModalV3
);
const
isOpen
=
state
.
isOpen
;
const
min
=
state
.
selectMin
||
0
;
const
max
=
state
.
selectMax
||
0
;
const
mustSelectOptions
=
state
.
mustSelectList
;
const
selectAbleOptions
=
state
.
selectAbleList
;
const
[
selectedOptions
,
setSelectedOptions
]
=
useState
([]);
const
overflow
=
state
.
overflow
;
const
LevelSum
=
state
.
allLevel
;
const
Level1Sum
=
mustSelectOptions
.
concat
(
selectedOptions
)
.
map
((
option
)
=>
option
.
level1
)
.
reduce
((
sum
,
current
)
=>
sum
+
current
,
0
);
const
Level2Sum
=
mustSelectOptions
.
concat
(
selectedOptions
)
.
map
((
option
)
=>
option
.
level2
)
.
reduce
((
sum
,
current
)
=>
sum
+
current
,
0
);
const
hint
=
useAppSelector
(
selectHint
);
const
preHintMsg
=
hint
?.
esHint
||
""
;
const
selectHintMsg
=
hint
?.
esSelectHint
||
"
请选择卡片
"
;
const
responseable
=
(
overflow
?
Level1Sum
>=
LevelSum
||
Level2Sum
>=
LevelSum
:
Level1Sum
==
LevelSum
||
Level2Sum
==
LevelSum
)
&&
selectedOptions
.
length
<=
max
&&
selectedOptions
.
length
>=
min
;
const
onFinish
=
()
=>
{
sendSelectCardResponse
(
mustSelectOptions
.
concat
(
selectedOptions
).
map
((
option
)
=>
option
.
response
)
);
dispatch
(
setCheckCardModalV3IsOpen
(
false
));
dispatch
(
resetCheckCardModalV3
());
dispatch
(
setCheckCardModalV3ResponseAble
(
false
));
};
return
(
<
DragModal
title=
{
`${preHintMsg} ${selectHintMsg} ${min}-${max}`
}
open=
{
isOpen
}
closable=
{
false
}
footer=
{
<>
<
Button
disabled=
{
!
responseable
}
onClick=
{
onFinish
}
>
finish
</
Button
>
</>
}
width=
{
800
}
>
<
CheckCard
.
Group
bordered
size=
"small"
multiple=
{
true
}
onChange=
{
(
values
:
any
)
=>
{
console
.
log
(
values
);
setSelectedOptions
(
values
);
}
}
>
<
Row
>
{
selectAbleOptions
.
map
((
option
,
idx
)
=>
{
return
(
<
Col
span=
{
4
}
key=
{
idx
}
>
<
CheckCard
title=
{
option
.
meta
.
text
.
name
}
description=
{
option
.
meta
.
text
.
desc
}
style=
{
{
width
:
120
}
}
cover=
{
<
img
alt=
{
option
.
meta
.
id
.
toString
()
}
src=
{
`${NeosConfig.cardImgUrl}/${option.meta.id}.jpg`
}
style=
{
{
width
:
100
}
}
/>
}
value=
{
option
}
/>
</
Col
>
);
})
}
</
Row
>
</
CheckCard
.
Group
>
<
p
>
必须选择的卡片
</
p
>
<
Row
>
{
mustSelectOptions
.
map
((
option
,
idx
)
=>
{
return
(
<
Col
span=
{
4
}
key=
{
idx
}
>
<
Card
hoverable
style=
{
{
width
:
120
}
}
cover=
{
<
img
alt=
{
option
.
meta
.
id
.
toString
()
}
src=
{
`${NeosConfig.cardImgUrl}/${option.meta.id}.jpg`
}
/>
}
/>
</
Col
>
);
})
}
</
Row
>
</
DragModal
>
);
};
export
default
CheckCardModalV3
;
src/ui/Duel/Message/CheckCounterModal.tsx
0 → 100644
View file @
db603083
import
{
Button
,
Row
,
Col
,
Card
,
InputNumber
}
from
"
antd
"
;
import
React
,
{
useState
}
from
"
react
"
;
import
{
sendSelectCounterResponse
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
fetchStrings
}
from
"
@/api/strings
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
clearCheckCounter
}
from
"
@/reducers/duel/mod
"
;
import
{
selectCheckCounterModal
}
from
"
@/reducers/duel/modal/checkCounterModalSlice
"
;
import
{
store
}
from
"
@/store
"
;
import
DragModal
from
"
./DragModal
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
const
CheckCounterModal
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
state
=
useAppSelector
(
selectCheckCounterModal
);
const
isOpen
=
state
.
isOpen
;
const
counterName
=
fetchStrings
(
"
!counter
"
,
`0x
${
state
.
counterType
!
}
`
);
const
min
=
state
.
min
||
0
;
const
options
=
state
.
options
;
const
[
selected
,
setSelected
]
=
useState
(
new
Array
(
options
.
length
));
const
sum
=
selected
.
reduce
((
sum
,
current
)
=>
sum
+
current
,
0
);
const
finishable
=
sum
==
min
;
const
onFinish
=
()
=>
{
sendSelectCounterResponse
(
selected
);
dispatch
(
clearCheckCounter
());
};
return
(
<
DragModal
title=
{
`请移除${min}个${counterName}`
}
open=
{
isOpen
}
closable=
{
false
}
footer=
{
<
Button
disabled=
{
!
finishable
}
onClick=
{
onFinish
}
>
finish
</
Button
>
}
>
<
Row
>
{
options
.
map
((
option
,
idx
)
=>
{
return
(
<
Col
span=
{
4
}
key=
{
idx
}
>
<
Card
hoverable
style=
{
{
width
:
120
}
}
cover=
{
<
img
alt=
{
option
.
code
.
toString
()
}
src=
{
`${NeosConfig.cardImgUrl}/${option.code}.jpg`
}
/>
}
>
<
InputNumber
min=
{
0
}
max=
{
option
.
max
}
defaultValue=
{
0
}
onChange=
{
(
value
)
=>
{
let
newSelected
=
[...
selected
];
newSelected
[
idx
]
=
value
||
0
;
setSelected
(
newSelected
);
}
}
/>
</
Card
>
</
Col
>
);
})
}
</
Row
>
</
DragModal
>
);
};
export
default
CheckCounterModal
;
src/ui/Duel/Message/DragModal.tsx
0 → 100644
View file @
db603083
// 经过封装的可拖拽`Modal`
import
React
,
{
useRef
,
useState
}
from
"
react
"
;
import
type
{
DraggableData
,
DraggableEvent
}
from
"
react-draggable
"
;
import
Draggable
from
"
react-draggable
"
;
import
{
Modal
,
ModalProps
}
from
"
antd
"
;
export
interface
DragModalProps
extends
ModalProps
{}
const
DragModal
=
(
props
:
DragModalProps
)
=>
{
const
dragRef
=
useRef
<
HTMLDivElement
>
(
null
);
const
[
bounds
,
setBounds
]
=
useState
({
left
:
0
,
top
:
0
,
bottom
:
0
,
right
:
0
,
});
const
onStart
=
(
_event
:
DraggableEvent
,
uiData
:
DraggableData
)
=>
{
const
{
clientWidth
,
clientHeight
}
=
window
.
document
.
documentElement
;
const
targetRect
=
dragRef
.
current
?.
getBoundingClientRect
();
if
(
!
targetRect
)
{
return
;
}
setBounds
({
left
:
-
targetRect
.
left
+
uiData
.
x
,
right
:
clientWidth
-
(
targetRect
.
right
-
uiData
.
x
),
top
:
-
targetRect
.
top
+
uiData
.
y
,
bottom
:
clientHeight
-
(
targetRect
.
bottom
-
uiData
.
y
),
});
};
return
(
<
Modal
{
...
props
}
modalRender=
{
(
modal
)
=>
(
<
Draggable
bounds=
{
bounds
}
onStart=
{
onStart
}
>
<
div
ref=
{
dragRef
}
>
{
modal
}
</
div
>
</
Draggable
>
)
}
>
{
props
.
children
}
</
Modal
>
);
};
export
default
DragModal
;
src/ui/Duel/Message/HintNotification.tsx
0 → 100644
View file @
db603083
import
React
,
{
useEffect
}
from
"
react
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
selectHint
}
from
"
@/reducers/duel/hintSlice
"
;
import
{
selectCurrentPhase
}
from
"
@/reducers/duel/phaseSlice
"
;
import
{
notification
}
from
"
antd
"
;
import
{
selectDuelResult
,
selectWaiting
}
from
"
@/reducers/duel/mod
"
;
import
{
useNavigate
}
from
"
react-router-dom
"
;
import
{
ygopro
}
from
"
@/api/ocgcore/idl/ocgcore
"
;
import
MsgWin
=
ygopro
.
StocGameMessage
.
MsgWin
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
const
HintNotification
=
()
=>
{
const
hint
=
useAppSelector
(
selectHint
);
const
currentPhase
=
useAppSelector
(
selectCurrentPhase
);
const
waiting
=
useAppSelector
(
selectWaiting
);
const
result
=
useAppSelector
(
selectDuelResult
);
const
navigate
=
useNavigate
();
const
[
api
,
contextHolder
]
=
notification
.
useNotification
({
maxCount
:
NeosConfig
.
ui
.
hint
.
maxCount
,
});
useEffect
(()
=>
{
if
(
hint
&&
hint
.
msg
)
{
api
.
info
({
message
:
`
${
hint
.
msg
}
`
,
placement
:
"
bottom
"
,
});
}
},
[
hint
?.
msg
]);
useEffect
(()
=>
{
if
(
currentPhase
)
{
api
.
info
({
message
:
`<当前阶段>
${
currentPhase
}
`
,
placement
:
"
topRight
"
,
});
}
},
[
currentPhase
]);
useEffect
(()
=>
{
if
(
waiting
)
{
api
.
info
({
message
:
"
...等待对方行动中...
"
,
placement
:
"
top
"
,
duration
:
NeosConfig
.
ui
.
hint
.
waitingDuration
,
});
}
},
[
waiting
]);
useEffect
(()
=>
{
if
(
result
)
{
const
message
=
result
==
MsgWin
.
ActionType
.
Win
?
"
胜利
"
:
MsgWin
.
ActionType
.
Defeated
?
"
失败
"
:
"
未知结果
"
;
api
.
info
({
message
,
placement
:
"
bottom
"
,
onClose
()
{
navigate
(
"
/
"
);
},
});
}
},
[
result
]);
return
<>
{
contextHolder
}
</>;
};
export
default
HintNotification
;
src/ui/Duel/Message/OptionModal.tsx
0 → 100644
View file @
db603083
import
React
,
{
useState
}
from
"
react
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
store
}
from
"
@/store
"
;
import
{
Button
}
from
"
antd
"
;
import
{
CheckCard
}
from
"
@ant-design/pro-components
"
;
import
{
selectOptionModalIsOpen
,
selectOptionModalOptions
,
}
from
"
@/reducers/duel/modal/mod
"
;
import
{
sendSelectOptionResponse
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
resetOptionModal
,
setOptionModalIsOpen
}
from
"
@/reducers/duel/mod
"
;
import
DragModal
from
"
./DragModal
"
;
const
OptionModal
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
isOpen
=
useAppSelector
(
selectOptionModalIsOpen
);
const
options
=
useAppSelector
(
selectOptionModalOptions
);
const
[
selected
,
setSelected
]
=
useState
<
number
|
undefined
>
(
undefined
);
return
(
<
DragModal
title=
"请选择需要发动的效果"
open=
{
isOpen
}
closable=
{
false
}
footer=
{
<
Button
disabled=
{
selected
===
undefined
}
onClick=
{
()
=>
{
if
(
selected
!==
undefined
)
{
sendSelectOptionResponse
(
selected
);
dispatch
(
setOptionModalIsOpen
(
false
));
dispatch
(
resetOptionModal
());
}
}
}
>
submit
</
Button
>
}
>
<
CheckCard
.
Group
bordered
size=
"small"
onChange=
{
(
value
)
=>
{
// @ts-ignore
setSelected
(
value
);
}
}
>
{
options
.
map
((
option
,
idx
)
=>
(
<
CheckCard
key=
{
idx
}
title=
{
option
.
msg
}
value=
{
option
.
response
}
/>
))
}
</
CheckCard
.
Group
>
</
DragModal
>
);
};
export
default
OptionModal
;
src/ui/Duel/Message/Phase.tsx
0 → 100644
View file @
db603083
import
React
,
{
useState
}
from
"
react
"
;
import
{
store
}
from
"
@/store
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
selectCurrentPhase
,
selectEnableBp
,
selectEnableEp
,
selectEnableM2
,
}
from
"
@/reducers/duel/phaseSlice
"
;
import
{
sendSelectBattleCmdResponse
,
sendSelectIdleCmdResponse
,
sendSurrender
,
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
clearAllIdleInteractivities
,
setEnableBp
,
setEnableEp
,
setEnableM2
,
}
from
"
@/reducers/duel/mod
"
;
import
{
Button
,
Modal
,
Space
}
from
"
antd
"
;
import
Icon
from
"
@ant-design/icons
"
;
import
{
ReactComponent
as
BattleSvg
}
from
"
../../../../neos-assets/crossed-swords.svg
"
;
import
{
ReactComponent
as
Main2Svg
}
from
"
../../../../neos-assets/sword-in-stone.svg
"
;
import
{
ReactComponent
as
EpSvg
}
from
"
../../../../neos-assets/power-button.svg
"
;
import
{
ReactComponent
as
SurrenderSvg
}
from
"
../../../../neos-assets/truce.svg
"
;
const
IconSize
=
"
150%
"
;
const
SpaceSize
=
16
;
const
PhaseButton
=
(
props
:
{
text
:
string
;
enable
:
boolean
;
onClick
:
()
=>
void
;
icon
?:
React
.
ReactNode
;
})
=>
{
return
(
<
Button
icon=
{
props
.
icon
}
disabled=
{
!
props
.
enable
}
onClick=
{
props
.
onClick
}
size=
"large"
>
{
props
.
text
}
</
Button
>
);
};
const
Phase
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
enableBp
=
useAppSelector
(
selectEnableBp
);
const
enableM2
=
useAppSelector
(
selectEnableM2
);
const
enableEp
=
useAppSelector
(
selectEnableEp
);
const
currentPhase
=
useAppSelector
(
selectCurrentPhase
);
const
[
modalOpen
,
setModalOpen
]
=
useState
(
false
);
const
response
=
currentPhase
===
"
BATTLE_START
"
||
currentPhase
===
"
BATTLE_STEP
"
||
currentPhase
===
"
DAMAGE
"
||
currentPhase
===
"
DAMAGE_GAL
"
||
currentPhase
===
"
BATTLE
"
?
3
:
7
;
const
onBp
=
()
=>
{
dispatch
(
clearAllIdleInteractivities
(
0
));
dispatch
(
clearAllIdleInteractivities
(
0
));
sendSelectIdleCmdResponse
(
6
);
dispatch
(
setEnableBp
(
false
));
};
const
onM2
=
()
=>
{
dispatch
(
clearAllIdleInteractivities
(
0
));
dispatch
(
clearAllIdleInteractivities
(
0
));
sendSelectBattleCmdResponse
(
2
);
dispatch
(
setEnableM2
(
false
));
};
const
onEp
=
()
=>
{
dispatch
(
clearAllIdleInteractivities
(
0
));
dispatch
(
clearAllIdleInteractivities
(
0
));
sendSelectIdleCmdResponse
(
response
);
dispatch
(
setEnableEp
(
false
));
};
const
onSurrender
=
()
=>
{
setModalOpen
(
true
);
};
return
(
<
Space
wrap
size=
{
SpaceSize
}
>
<
PhaseButton
icon=
{
<
Icon
component=
{
BattleSvg
}
style=
{
{
fontSize
:
IconSize
}
}
/>
}
enable=
{
enableBp
}
text=
"战斗阶段"
onClick=
{
onBp
}
/>
<
PhaseButton
icon=
{
<
Icon
component=
{
Main2Svg
}
style=
{
{
fontSize
:
IconSize
}
}
/>
}
enable=
{
enableM2
}
text=
"主要阶段2"
onClick=
{
onM2
}
/>
<
PhaseButton
icon=
{
<
Icon
component=
{
EpSvg
}
style=
{
{
fontSize
:
IconSize
}
}
/>
}
enable=
{
enableEp
}
text=
"结束回合"
onClick=
{
onEp
}
/>
<
PhaseButton
icon=
{
<
Icon
component=
{
SurrenderSvg
}
style=
{
{
fontSize
:
IconSize
}
}
/>
}
enable=
{
true
}
text=
"投降"
onClick=
{
onSurrender
}
/>
<
Modal
title=
"是否确认要投降?"
open=
{
modalOpen
}
closable=
{
false
}
footer=
{
<>
<
Button
onClick=
{
()
=>
{
sendSurrender
();
setModalOpen
(
false
);
}
}
>
Yes
</
Button
>
<
Button
onClick=
{
()
=>
{
setModalOpen
(
false
);
}
}
>
No
</
Button
>
</>
}
/>
</
Space
>
);
};
export
default
Phase
;
src/ui/Duel/Message/PositionModal.tsx
0 → 100644
View file @
db603083
import
React
,
{
useState
}
from
"
react
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
store
}
from
"
@/store
"
;
import
{
Button
}
from
"
antd
"
;
import
{
sendSelectPositionResponse
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
selectPositionModalIsOpen
,
selectPositionModalPositions
,
}
from
"
@/reducers/duel/modal/mod
"
;
import
{
ygopro
}
from
"
@/api/ocgcore/idl/ocgcore
"
;
import
{
resetPositionModal
,
setPositionModalIsOpen
,
}
from
"
@/reducers/duel/mod
"
;
import
{
CheckCard
}
from
"
@ant-design/pro-components
"
;
import
DragModal
from
"
./DragModal
"
;
const
PositionModal
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
isOpen
=
useAppSelector
(
selectPositionModalIsOpen
);
const
positions
=
useAppSelector
(
selectPositionModalPositions
);
const
[
selected
,
setSelected
]
=
useState
<
ygopro
.
CardPosition
|
undefined
>
(
undefined
);
return
(
<
DragModal
title=
"请选择表示形式"
open=
{
isOpen
}
closable=
{
false
}
footer=
{
<
Button
disabled=
{
selected
===
undefined
}
onClick=
{
()
=>
{
if
(
selected
!==
undefined
)
{
sendSelectPositionResponse
(
selected
);
dispatch
(
setPositionModalIsOpen
(
false
));
dispatch
(
resetPositionModal
());
}
}
}
>
submit
</
Button
>
}
>
<
CheckCard
.
Group
bordered
size=
"small"
onChange=
{
(
value
)
=>
{
// @ts-ignore
setSelected
(
value
);
}
}
>
{
positions
.
map
((
position
,
idx
)
=>
(
<
CheckCard
key=
{
idx
}
title=
{
cardPositionToChinese
(
position
)
}
value=
{
position
}
/>
))
}
</
CheckCard
.
Group
>
</
DragModal
>
);
};
function
cardPositionToChinese
(
position
:
ygopro
.
CardPosition
):
string
{
switch
(
position
)
{
case
ygopro
.
CardPosition
.
FACEUP_ATTACK
:
{
return
"
正面攻击形式
"
;
}
case
ygopro
.
CardPosition
.
FACEUP_DEFENSE
:
{
return
"
正面防守形式
"
;
}
case
ygopro
.
CardPosition
.
FACEDOWN_ATTACK
:
{
return
"
背面攻击形式
"
;
}
case
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
:
{
return
"
背面防守形式
"
;
}
default
:
{
return
"
[?]
"
;
}
}
}
export
default
PositionModal
;
src/ui/Duel/Message/SendBox.tsx
0 → 100644
View file @
db603083
import
React
,
{
useState
}
from
"
react
"
;
import
{
Input
,
Button
,
Row
,
Col
}
from
"
antd
"
;
import
{
SendOutlined
}
from
"
@ant-design/icons
"
;
import
{
sendChat
}
from
"
@/api/ocgcore/ocgHelper
"
;
const
SendBox
=
()
=>
{
const
[
content
,
setContent
]
=
useState
(
""
);
return
(
<>
<
Row
>
<
Input
.
TextArea
placeholder=
"Message to sent..."
autoSize=
{
{
minRows
:
3
,
maxRows
:
4
}
}
value=
{
content
}
onChange=
{
(
e
)
=>
{
setContent
(
e
.
target
.
value
);
}
}
/>
</
Row
>
<
Row
>
<
Col
>
<
Button
icon=
{
<
SendOutlined
/>
}
onClick=
{
()
=>
{
sendChat
(
content
);
setContent
(
""
);
}
}
disabled=
{
!
content
}
/>
</
Col
>
</
Row
>
</>
);
};
export
default
SendBox
;
src/ui/Duel/Message/SortCardModal.tsx
0 → 100644
View file @
db603083
import
React
,
{
useEffect
,
useState
}
from
"
react
"
;
import
{
DndContext
,
closestCenter
,
KeyboardSensor
,
PointerSensor
,
useSensor
,
useSensors
,
DragEndEvent
,
}
from
"
@dnd-kit/core
"
;
import
{
arrayMove
,
SortableContext
,
sortableKeyboardCoordinates
,
verticalListSortingStrategy
,
useSortable
,
}
from
"
@dnd-kit/sortable
"
;
import
{
CSS
}
from
"
@dnd-kit/utilities
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
selectSortCardModal
}
from
"
@/reducers/duel/modal/sortCardModalSlice
"
;
import
{
sendSortCardResponse
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
store
}
from
"
@/store
"
;
import
{
resetSortCardModal
}
from
"
@/reducers/duel/mod
"
;
import
{
Modal
,
Button
,
Card
}
from
"
antd
"
;
import
{
CardMeta
}
from
"
@/api/cards
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
const
SortCardModal
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
state
=
useAppSelector
(
selectSortCardModal
);
const
isOpen
=
state
.
isOpen
;
const
options
=
state
.
options
;
const
[
items
,
setItems
]
=
useState
(
options
);
const
sensors
=
useSensors
(
useSensor
(
PointerSensor
),
useSensor
(
KeyboardSensor
,
{
coordinateGetter
:
sortableKeyboardCoordinates
,
})
);
const
onFinish
=
()
=>
{
sendSortCardResponse
(
items
.
map
((
item
)
=>
item
.
response
));
dispatch
(
resetSortCardModal
());
};
const
onDragEnd
=
(
event
:
DragEndEvent
)
=>
{
const
{
active
,
over
}
=
event
;
if
(
active
.
id
!==
over
?.
id
)
{
setItems
((
items
)
=>
{
const
oldIndex
=
items
.
findIndex
((
item
)
=>
item
.
response
==
active
.
id
);
const
newIndex
=
items
.
findIndex
((
item
)
=>
item
.
response
===
over
?.
id
);
return
arrayMove
(
items
,
oldIndex
,
newIndex
);
});
}
};
useEffect
(()
=>
{
setItems
(
options
);
},
[
options
]);
return
(
<
Modal
title=
"请为下列卡牌排序"
open=
{
isOpen
}
closable=
{
false
}
footer=
{
<
Button
onClick=
{
onFinish
}
>
finish
</
Button
>
}
>
<
DndContext
sensors=
{
sensors
}
collisionDetection=
{
closestCenter
}
onDragEnd=
{
onDragEnd
}
>
<
SortableContext
items=
{
items
.
map
((
item
)
=>
item
.
response
)
}
strategy=
{
verticalListSortingStrategy
}
>
{
items
.
map
((
item
)
=>
(
<
SortableItem
key=
{
item
.
response
}
id=
{
item
.
response
}
meta=
{
item
.
meta
}
/>
))
}
</
SortableContext
>
</
DndContext
>
</
Modal
>
);
};
const
SortableItem
=
(
props
:
{
id
:
number
;
meta
:
CardMeta
})
=>
{
const
{
attributes
,
listeners
,
setNodeRef
,
transform
,
transition
}
=
useSortable
({
id
:
props
.
id
});
const
style
=
{
transform
:
CSS
.
Transform
.
toString
(
transform
),
transition
,
};
return
(
<
div
ref=
{
setNodeRef
}
style=
{
style
}
{
...
attributes
}
{
...
listeners
}
>
<
Card
style=
{
{
width
:
100
}
}
cover=
{
<
img
alt=
{
props
.
meta
.
id
.
toString
()
}
src=
{
`${NeosConfig.cardImgUrl}/${props.meta.id}.jpg`
}
/>
}
/>
</
div
>
);
};
export
default
SortCardModal
;
src/ui/Duel/Message/Status.tsx
0 → 100644
View file @
db603083
import
React
from
"
react
"
;
import
{
UserOutlined
}
from
"
@ant-design/icons
"
;
import
{
Avatar
}
from
"
antd
"
;
import
{
CheckCard
}
from
"
@ant-design/pro-components
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
selectMeInitInfo
,
selectOpInitInfo
,
}
from
"
@/reducers/duel/initInfoSlice
"
;
import
{
selectWaiting
}
from
"
@/reducers/duel/mod
"
;
const
Config
=
NeosConfig
.
ui
.
status
;
const
avatarSize
=
40
;
const
ME_VALUE
=
"
myself
"
;
const
OP_VALUE
=
"
opponent
"
;
const
PlayerStatus
=
()
=>
{
const
meInfo
=
useAppSelector
(
selectMeInitInfo
);
const
opInfo
=
useAppSelector
(
selectOpInitInfo
);
const
waiting
=
useAppSelector
(
selectWaiting
)
||
false
;
return
(
<
CheckCard
.
Group
bordered
style=
{
{
height
:
`${NeosConfig.ui.layout.header.height}`
}
}
value=
{
waiting
?
OP_VALUE
:
ME_VALUE
}
>
<
CheckCard
avatar=
{
<
Avatar
size=
{
avatarSize
}
style=
{
{
backgroundColor
:
Config
.
opAvatarColor
}
}
icon=
{
<
UserOutlined
/>
}
/>
}
title=
{
OP_VALUE
}
description=
{
`Lp: ${opInfo?.life || 0}`
}
value=
{
OP_VALUE
}
style=
{
{
position
:
"
absolute
"
,
left
:
`${NeosConfig.ui.layout.sider.width}px`
,
}
}
/>
<
CheckCard
avatar=
{
<
Avatar
size=
{
avatarSize
}
style=
{
{
backgroundColor
:
Config
.
meAvatarColor
}
}
icon=
{
<
UserOutlined
/>
}
/>
}
title=
{
ME_VALUE
}
description=
{
`Lp: ${meInfo?.life || 0}`
}
value=
{
ME_VALUE
}
style=
{
{
position
:
"
absolute
"
,
right
:
"
0px
"
,
}
}
/>
</
CheckCard
.
Group
>
);
};
export
default
PlayerStatus
;
src/ui/Duel/Message/TimeLine.tsx
0 → 100644
View file @
db603083
import
React
,
{
useEffect
,
useState
}
from
"
react
"
;
import
{
Timeline
,
TimelineItemProps
}
from
"
antd
"
;
import
{
MessageOutlined
}
from
"
@ant-design/icons
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
selectChat
}
from
"
@/reducers/chatSlice
"
;
const
DuelTimeLine
=
()
=>
{
const
[
items
,
setItems
]
=
useState
<
TimelineItemProps
[]
>
([]);
const
chat
=
useAppSelector
(
selectChat
);
useEffect
(()
=>
{
setItems
((
prev
)
=>
prev
.
concat
([
{
dot
:
<
MessageOutlined
/>,
children
:
chat
,
color
:
"
green
"
,
},
])
);
},
[
chat
]);
return
<
Timeline
items=
{
items
}
/>;
};
export
default
DuelTimeLine
;
src/ui/Duel/Message/YesNoModal.tsx
0 → 100644
View file @
db603083
import
React
from
"
react
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
store
}
from
"
@/store
"
;
import
{
Button
}
from
"
antd
"
;
import
{
sendSelectEffectYnResponse
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
selectYesNoModalIsOpen
,
selectYesNOModalMsg
,
}
from
"
@/reducers/duel/modal/mod
"
;
import
{
setYesNoModalIsOpen
}
from
"
@/reducers/duel/mod
"
;
import
DragModal
from
"
./DragModal
"
;
import
{
selectHint
}
from
"
@/reducers/duel/hintSlice
"
;
const
YesNoModal
=
()
=>
{
const
dispatch
=
store
.
dispatch
;
const
isOpen
=
useAppSelector
(
selectYesNoModalIsOpen
);
const
msg
=
useAppSelector
(
selectYesNOModalMsg
);
const
hint
=
useAppSelector
(
selectHint
);
const
preHintMsg
=
hint
?.
esHint
||
""
;
return
(
<
DragModal
title=
{
`${preHintMsg} ${msg}`
}
open=
{
isOpen
}
closable=
{
false
}
footer=
{
<>
<
Button
onClick=
{
()
=>
{
sendSelectEffectYnResponse
(
true
);
dispatch
(
setYesNoModalIsOpen
(
false
));
}
}
>
Yes
</
Button
>
<
Button
onClick=
{
()
=>
{
sendSelectEffectYnResponse
(
false
);
dispatch
(
setYesNoModalIsOpen
(
false
));
}
}
>
No
</
Button
>
</>
}
/>
);
};
export
default
YesNoModal
;
src/ui/Duel/PlayMat/Deck.tsx
0 → 100644
View file @
db603083
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
selectMeDeck
,
selectOpDeck
}
from
"
@/reducers/duel/deckSlice
"
;
import
SingleSlot
,
{
Depth
}
from
"
./SingleSlot
"
;
import
{
cardSlotRotation
}
from
"
../utils
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
const
CommonDeck
=
()
=>
{
const
meDeck
=
useAppSelector
(
selectMeDeck
).
inner
;
const
opDeck
=
useAppSelector
(
selectOpDeck
).
inner
;
return
(
<>
<
SingleSlot
state=
{
meDeck
}
position=
{
deckPosition
(
0
,
meDeck
.
length
)
}
rotation=
{
cardSlotRotation
(
false
)
}
/>
<
SingleSlot
state=
{
opDeck
}
position=
{
deckPosition
(
1
,
opDeck
.
length
)
}
rotation=
{
cardSlotRotation
(
true
)
}
/>
</>
);
};
const
deckPosition
=
(
player
:
number
,
deckLength
:
number
)
=>
{
const
x
=
player
==
0
?
3.2
:
-
3.2
;
const
y
=
(
Depth
*
deckLength
)
/
2
+
NeosConfig
.
ui
.
card
.
floating
;
const
z
=
player
==
0
?
-
3.3
:
3.3
;
return
new
BABYLON
.
Vector3
(
x
,
y
,
z
);
};
export
default
CommonDeck
;
src/ui/Duel/PlayMat/ExtraDeck.tsx
0 → 100644
View file @
db603083
import
SingleSlot
,
{
Depth
}
from
"
./SingleSlot
"
;
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
selectMeExtraDeck
,
selectOpExtraDeck
,
}
from
"
@/reducers/duel/extraDeckSlice
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
import
{
cardSlotRotation
}
from
"
../utils
"
;
const
ExtraDeck
=
()
=>
{
const
meExtraDeck
=
useAppSelector
(
selectMeExtraDeck
).
inner
;
const
opExtraDeck
=
useAppSelector
(
selectOpExtraDeck
).
inner
;
return
(
<>
<
SingleSlot
state=
{
meExtraDeck
}
position=
{
extraDeckPosition
(
0
,
meExtraDeck
.
length
)
}
rotation=
{
cardSlotRotation
(
false
)
}
/>
<
SingleSlot
state=
{
opExtraDeck
}
position=
{
extraDeckPosition
(
1
,
opExtraDeck
.
length
)
}
rotation=
{
cardSlotRotation
(
true
)
}
/>
</>
);
};
const
extraDeckPosition
=
(
player
:
number
,
deckLength
:
number
)
=>
{
const
x
=
player
==
0
?
-
3.3
:
3.3
;
const
y
=
(
Depth
&
deckLength
)
/
2
+
NeosConfig
.
ui
.
card
.
floating
;
const
z
=
player
==
0
?
-
3.3
:
3.3
;
return
new
BABYLON
.
Vector3
(
x
,
y
,
z
);
};
export
default
ExtraDeck
;
src/ui/Duel/PlayMat/Field.tsx
0 → 100644
View file @
db603083
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
selectMeMagics
,
selectOpMagics
}
from
"
@/reducers/duel/magicSlice
"
;
import
{
clearMagicPlaceInteractivities
}
from
"
@/reducers/duel/mod
"
;
import
FixedSlot
from
"
./FixedSlot
"
;
import
{
Depth
}
from
"
./SingleSlot
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
import
{
cardSlotRotation
}
from
"
../utils
"
;
const
Field
=
()
=>
{
const
meField
=
useAppSelector
(
selectMeMagics
).
inner
.
find
(
(
_
,
sequence
)
=>
sequence
==
5
);
const
opField
=
useAppSelector
(
selectOpMagics
).
inner
.
find
(
(
_
,
sequence
)
=>
sequence
==
5
);
return
(
<>
{
meField
?
(
<
FixedSlot
state=
{
meField
}
sequence=
{
0
}
position=
{
fieldPosition
(
0
)
}
rotation=
{
cardSlotRotation
(
false
)
}
clearPlaceInteractivitiesAction=
{
clearMagicPlaceInteractivities
}
/>
)
:
(
<></>
)
}
{
opField
?
(
<
FixedSlot
state=
{
opField
}
sequence=
{
0
}
position=
{
fieldPosition
(
1
)
}
rotation=
{
cardSlotRotation
(
true
)
}
clearPlaceInteractivitiesAction=
{
clearMagicPlaceInteractivities
}
/>
)
:
(
<></>
)
}
</>
);
};
const
fieldPosition
=
(
player
:
number
)
=>
{
const
x
=
player
==
0
?
-
3.3
:
3.3
;
const
y
=
Depth
/
2
+
NeosConfig
.
ui
.
card
.
floating
;
const
z
=
player
==
0
?
-
2.0
:
2.0
;
return
new
BABYLON
.
Vector3
(
x
,
y
,
z
);
};
export
default
Field
;
src/ui/Duel/PlayMat/FixedSlot.tsx
0 → 100644
View file @
db603083
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
{
store
}
from
"
@/store
"
;
import
{
CardState
}
from
"
@/reducers/duel/generic
"
;
import
{
useRef
}
from
"
react
"
;
import
{
useClick
}
from
"
@/hook
"
;
import
{
sendSelectPlaceResponse
}
from
"
@/api/ocgcore/ocgHelper
"
;
import
{
ygopro
}
from
"
@/api/ocgcore/idl/ocgcore
"
;
import
{
setCardListModalInfo
,
setCardListModalIsOpen
,
setCardModalCounters
,
setCardModalInteractivies
,
setCardModalIsOpen
,
setCardModalMeta
,
}
from
"
@/reducers/duel/mod
"
;
import
{
ActionCreatorWithPayload
}
from
"
@reduxjs/toolkit
"
;
import
{
interactTypeToString
}
from
"
../utils
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
const
transform
=
NeosConfig
.
ui
.
card
.
transform
;
const
defenceRotation
=
NeosConfig
.
ui
.
card
.
defenceRotation
;
const
cardDefenceRotation
=
new
BABYLON
.
Vector3
(
defenceRotation
.
x
,
defenceRotation
.
y
,
defenceRotation
.
z
);
const
FixedSlot
=
(
props
:
{
state
:
CardState
;
sequence
:
number
;
position
:
BABYLON
.
Vector3
;
rotation
:
BABYLON
.
Vector3
;
deffenseRotation
?:
BABYLON
.
Vector3
;
clearPlaceInteractivitiesAction
:
ActionCreatorWithPayload
<
number
,
string
>
;
})
=>
{
const
planeRef
=
useRef
(
null
);
const
rotation
=
props
.
state
.
location
.
position
===
ygopro
.
CardPosition
.
DEFENSE
||
props
.
state
.
location
.
position
===
ygopro
.
CardPosition
.
FACEUP_DEFENSE
||
props
.
state
.
location
.
position
===
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
?
props
.
deffenseRotation
||
cardDefenceRotation
:
props
.
rotation
;
const
edgesWidth
=
2.0
;
const
edgesColor
=
BABYLON
.
Color4
.
FromColor3
(
BABYLON
.
Color3
.
Yellow
());
const
dispatch
=
store
.
dispatch
;
const
faceDown
=
props
.
state
.
location
.
position
===
ygopro
.
CardPosition
.
FACEDOWN_DEFENSE
||
props
.
state
.
location
.
position
===
ygopro
.
CardPosition
.
FACEDOWN_ATTACK
||
props
.
state
.
location
.
position
===
ygopro
.
CardPosition
.
FACEDOWN
;
useClick
(
(
_event
)
=>
{
if
(
props
.
state
.
placeInteractivities
)
{
sendSelectPlaceResponse
(
props
.
state
.
placeInteractivities
.
response
);
dispatch
(
props
.
clearPlaceInteractivitiesAction
(
0
));
dispatch
(
props
.
clearPlaceInteractivitiesAction
(
1
));
}
else
if
(
props
.
state
.
occupant
)
{
// 中央弹窗展示选中卡牌信息
dispatch
(
setCardModalMeta
(
props
.
state
.
occupant
));
dispatch
(
setCardModalInteractivies
(
props
.
state
.
idleInteractivities
.
map
((
interactivity
)
=>
{
return
{
desc
:
interactTypeToString
(
interactivity
.
interactType
),
response
:
interactivity
.
response
,
};
})
)
);
dispatch
(
setCardModalCounters
(
props
.
state
.
counters
));
dispatch
(
setCardModalIsOpen
(
true
));
// 侧边栏展示超量素材信息
if
(
props
.
state
.
overlay_materials
&&
props
.
state
.
overlay_materials
.
length
>
0
)
{
dispatch
(
setCardListModalInfo
(
props
.
state
.
overlay_materials
?.
map
((
overlay
)
=>
{
return
{
meta
:
overlay
,
interactivies
:
[],
};
})
||
[]
)
);
dispatch
(
setCardListModalIsOpen
(
true
));
}
}
},
planeRef
,
[
props
.
state
]
);
return
(
<
plane
name=
{
`fixedslot-${props.sequence}`
}
ref=
{
planeRef
}
width=
{
transform
.
x
}
height=
{
transform
.
y
}
position=
{
props
.
position
}
rotation=
{
rotation
}
enableEdgesRendering
edgesWidth=
{
props
.
state
.
placeInteractivities
||
props
.
state
.
idleInteractivities
.
length
>
0
?
edgesWidth
:
0
}
edgesColor=
{
edgesColor
}
>
<
standardMaterial
name=
{
`fixedslot-mat-${props.sequence}`
}
diffuseTexture=
{
props
.
state
.
occupant
?
faceDown
?
new
BABYLON
.
Texture
(
`${NeosConfig.assetsPath}/card_back.jpg`
)
:
new
BABYLON
.
Texture
(
`${NeosConfig.cardImgUrl}/${props.state.occupant.id}.jpg`
)
:
new
BABYLON
.
Texture
(
`${NeosConfig.assetsPath}/card_slot.png`
)
}
alpha=
{
props
.
state
.
occupant
?
1
:
0
}
></
standardMaterial
>
</
plane
>
);
};
export
default
FixedSlot
;
src/ui/Duel/PlayMat/Hands.tsx
0 → 100644
View file @
db603083
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
{
useAppSelector
,
useClick
}
from
"
@/hook
"
;
import
{
selectMeHands
,
selectOpHands
}
from
"
@/reducers/duel/handsSlice
"
;
import
{
CardState
}
from
"
@/reducers/duel/generic
"
;
import
{
setCardModalIsOpen
,
setCardModalMeta
,
setCardModalInteractivies
,
}
from
"
@/reducers/duel/mod
"
;
import
{
store
}
from
"
@/store
"
;
import
{
useHover
}
from
"
react-babylonjs
"
;
import
{
useState
,
useRef
,
useEffect
}
from
"
react
"
;
import
{
useSpring
,
animated
}
from
"
../spring
"
;
import
{
zip
,
interactTypeToString
}
from
"
../utils
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
const
groundShape
=
NeosConfig
.
ui
.
ground
;
const
left
=
-
(
groundShape
.
width
/
2
);
const
handShape
=
NeosConfig
.
ui
.
card
.
transform
;
const
rotation
=
NeosConfig
.
ui
.
card
.
handRotation
;
const
handRotation
=
new
BABYLON
.
Vector3
(
rotation
.
x
,
rotation
.
y
,
rotation
.
z
);
const
hoverScaling
=
NeosConfig
.
ui
.
card
.
handHoverScaling
;
const
Hands
=
()
=>
{
const
meHands
=
useAppSelector
(
selectMeHands
).
inner
;
const
meHandPositions
=
handPositons
(
0
,
meHands
);
const
opHands
=
useAppSelector
(
selectOpHands
).
inner
;
const
opHandPositions
=
handPositons
(
1
,
opHands
);
return
(
<>
{
zip
(
meHands
,
meHandPositions
).
map
(([
hand
,
position
],
idx
)
=>
{
return
(
<
CHand
key=
{
idx
}
state=
{
hand
}
sequence=
{
idx
}
position=
{
position
}
rotation=
{
handRotation
}
cover=
{
(
id
)
=>
`${NeosConfig.cardImgUrl}/${id}.jpg`
}
/>
);
})
}
{
zip
(
opHands
,
opHandPositions
).
map
(([
hand
,
position
],
idx
)
=>
{
return
(
<
CHand
key=
{
idx
}
state=
{
hand
}
sequence=
{
idx
}
position=
{
position
}
rotation=
{
handRotation
}
cover=
{
(
_
)
=>
`${NeosConfig.assetsPath}/card_back.jpg`
}
/>
);
})
}
</>
);
};
const
CHand
=
(
props
:
{
state
:
CardState
;
sequence
:
number
;
position
:
BABYLON
.
Vector3
;
rotation
:
BABYLON
.
Vector3
;
cover
:
(
id
:
number
)
=>
string
;
})
=>
{
const
hoverScale
=
new
BABYLON
.
Vector3
(
hoverScaling
.
x
,
hoverScaling
.
y
,
hoverScaling
.
z
);
const
defaultScale
=
new
BABYLON
.
Vector3
(
1
,
1
,
1
);
const
edgesWidth
=
2.0
;
const
edgesColor
=
BABYLON
.
Color4
.
FromColor3
(
BABYLON
.
Color3
.
Yellow
());
const
planeRef
=
useRef
(
null
);
const
state
=
props
.
state
;
const
[
hovered
,
setHovered
]
=
useState
(
false
);
const
position
=
props
.
position
;
const
dispatch
=
store
.
dispatch
;
const
[
spring
,
api
]
=
useSpring
(
()
=>
({
from
:
{
position
,
},
config
:
{
mass
:
1.0
,
tension
:
170
,
friction
:
900
,
precision
:
0.01
,
velocity
:
0.0
,
clamp
:
true
,
duration
:
2000
,
},
}),
[]
);
useEffect
(()
=>
{
api
.
start
({
position
,
});
},
[
position
]);
useHover
(
()
=>
setHovered
(
true
),
()
=>
setHovered
(
false
),
planeRef
);
useClick
(
()
=>
{
if
(
state
.
occupant
)
{
dispatch
(
setCardModalMeta
(
state
.
occupant
));
}
dispatch
(
setCardModalInteractivies
(
state
.
idleInteractivities
.
map
((
interactive
)
=>
{
return
{
desc
:
interactTypeToString
(
interactive
.
interactType
),
response
:
interactive
.
response
,
};
})
)
);
dispatch
(
setCardModalIsOpen
(
true
));
},
planeRef
,
[
state
]
);
return
(
// @ts-ignore
<
animated
.
transformNode
name=
""
>
<
animated
.
plane
name=
{
`hand-${props.sequence}`
}
ref=
{
planeRef
}
width=
{
handShape
.
x
}
height=
{
handShape
.
y
}
scaling=
{
hovered
?
hoverScale
:
defaultScale
}
position=
{
spring
.
position
}
rotation=
{
props
.
rotation
}
enableEdgesRendering
edgesWidth=
{
state
.
idleInteractivities
.
length
>
0
||
state
.
placeInteractivities
?
edgesWidth
:
0
}
edgesColor=
{
edgesColor
}
>
<
animated
.
standardMaterial
name=
{
`hand-mat-${props.sequence}`
}
diffuseTexture=
{
new
BABYLON
.
Texture
(
props
.
cover
(
state
.
occupant
?.
id
||
0
))
}
/>
</
animated
.
plane
>
</
animated
.
transformNode
>
);
};
const
handPositons
=
(
player
:
number
,
hands
:
CardState
[])
=>
{
const
gap
=
groundShape
.
width
/
(
hands
.
length
-
1
);
const
x
=
(
idx
:
number
)
=>
player
==
0
?
left
+
gap
*
idx
:
-
left
-
gap
*
idx
;
const
y
=
handShape
.
y
/
2
;
const
z
=
player
==
0
?
-
(
groundShape
.
height
/
2
)
-
1
:
groundShape
.
height
/
2
+
1
;
return
hands
.
map
((
_
,
idx
)
=>
new
BABYLON
.
Vector3
(
x
(
idx
),
y
,
z
));
};
export
default
Hands
;
src/ui/Duel/PlayMat/Magics.tsx
0 → 100644
View file @
db603083
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
{
selectMeMagics
,
selectOpMagics
}
from
"
@/reducers/duel/magicSlice
"
;
import
{
CardState
}
from
"
@/reducers/duel/generic
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
zip
,
cardSlotRotation
}
from
"
../utils
"
;
import
FixedSlot
from
"
./FixedSlot
"
;
import
{
clearMagicPlaceInteractivities
}
from
"
@/reducers/duel/mod
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
// TODO: use config
const
left
=
-
2.15
;
const
gap
=
1.05
;
const
transform
=
NeosConfig
.
ui
.
card
.
transform
;
const
Magics
=
()
=>
{
const
meMagics
=
useAppSelector
(
selectMeMagics
).
inner
;
const
meMagicPositions
=
magicPositions
(
0
,
meMagics
);
const
opMagics
=
useAppSelector
(
selectOpMagics
).
inner
;
const
opMagicPositions
=
magicPositions
(
1
,
opMagics
);
return
(
<>
{
zip
(
meMagics
,
meMagicPositions
)
.
slice
(
0
,
5
)
.
map
(([
magic
,
position
],
sequence
)
=>
{
return
(
<
FixedSlot
state=
{
magic
}
key=
{
sequence
}
sequence=
{
sequence
}
position=
{
position
}
rotation=
{
cardSlotRotation
(
false
)
}
clearPlaceInteractivitiesAction=
{
clearMagicPlaceInteractivities
}
/>
);
})
}
{
zip
(
opMagics
,
opMagicPositions
)
.
slice
(
0
,
5
)
.
map
(([
magic
,
position
],
sequence
)
=>
{
return
(
<
FixedSlot
state=
{
magic
}
key=
{
sequence
}
sequence=
{
sequence
}
position=
{
position
}
rotation=
{
cardSlotRotation
(
true
)
}
clearPlaceInteractivitiesAction=
{
clearMagicPlaceInteractivities
}
/>
);
})
}
</>
);
};
const
magicPositions
=
(
player
:
number
,
magics
:
CardState
[])
=>
{
const
x
=
(
sequence
:
number
)
=>
player
==
0
?
left
+
gap
*
sequence
:
-
left
-
gap
*
sequence
;
const
y
=
transform
.
z
/
2
+
NeosConfig
.
ui
.
card
.
floating
;
const
z
=
player
==
0
?
-
2.6
:
2.6
;
return
magics
.
map
((
_
,
sequence
)
=>
new
BABYLON
.
Vector3
(
x
(
sequence
),
y
,
z
));
};
export
default
Magics
;
src/ui/Duel/PlayMat/Monsters.tsx
0 → 100644
View file @
db603083
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
{
CardState
}
from
"
@/reducers/duel/generic
"
;
import
"
react-babylonjs
"
;
import
{
useAppSelector
}
from
"
@/hook
"
;
import
{
selectMeMonsters
,
selectOpMonsters
,
}
from
"
@/reducers/duel/monstersSlice
"
;
import
{
zip
,
cardSlotRotation
,
cardSlotDefenceRotation
}
from
"
../utils
"
;
import
FixedSlot
from
"
./FixedSlot
"
;
import
{
clearMonsterPlaceInteractivities
}
from
"
@/reducers/duel/mod
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
const
transform
=
NeosConfig
.
ui
.
card
.
transform
;
const
floating
=
NeosConfig
.
ui
.
card
.
floating
;
const
left
=
-
2.15
;
// TODO: config
const
gap
=
1.05
;
const
Monsters
=
()
=>
{
const
meMonsters
=
useAppSelector
(
selectMeMonsters
).
inner
;
const
meMonsterPositions
=
monsterPositions
(
0
,
meMonsters
);
const
opMonsters
=
useAppSelector
(
selectOpMonsters
).
inner
;
const
opMonsterPositions
=
monsterPositions
(
1
,
opMonsters
);
return
(
<>
{
zip
(
meMonsters
,
meMonsterPositions
)
.
slice
(
0
,
5
)
.
map
(([
monster
,
position
],
sequence
)
=>
(
<
FixedSlot
state=
{
monster
}
key=
{
sequence
}
sequence=
{
sequence
}
position=
{
position
}
rotation=
{
cardSlotRotation
(
false
)
}
deffenseRotation=
{
cardSlotDefenceRotation
()
}
clearPlaceInteractivitiesAction=
{
clearMonsterPlaceInteractivities
}
/>
))
}
{
zip
(
opMonsters
,
opMonsterPositions
)
.
slice
(
0
,
5
)
.
map
(([
monster
,
position
],
sequence
)
=>
(
<
FixedSlot
state=
{
monster
}
key=
{
sequence
}
sequence=
{
sequence
}
position=
{
position
}
rotation=
{
cardSlotRotation
(
true
)
}
deffenseRotation=
{
cardSlotDefenceRotation
()
}
clearPlaceInteractivitiesAction=
{
clearMonsterPlaceInteractivities
}
/>
))
}
<
ExtraMonsters
meMonsters=
{
meMonsters
}
opMonsters=
{
opMonsters
}
/>
</>
);
};
// TODO: use props and redux
const
ExtraMonsters
=
(
props
:
{
meMonsters
:
CardState
[];
opMonsters
:
CardState
[];
})
=>
{
const
meLeft
=
props
.
meMonsters
.
find
((
_
,
sequence
)
=>
sequence
==
5
);
const
meRight
=
props
.
meMonsters
.
find
((
_
,
sequence
)
=>
sequence
==
6
);
const
opLeft
=
props
.
opMonsters
.
find
((
_
,
sequence
)
=>
sequence
==
5
);
const
opRight
=
props
.
opMonsters
.
find
((
_
,
sequence
)
=>
sequence
==
6
);
const
leftPosition
=
new
BABYLON
.
Vector3
(
-
1.1
,
transform
.
z
/
2
+
floating
,
0
);
const
rightPosition
=
new
BABYLON
.
Vector3
(
1.1
,
transform
.
z
/
2
+
floating
,
0
);
const
meRotation
=
cardSlotRotation
(
false
);
const
opRotation
=
cardSlotRotation
(
true
);
return
(
<>
{
meLeft
?
(
<
FixedSlot
state=
{
meLeft
}
sequence=
{
5
}
position=
{
leftPosition
}
rotation=
{
meRotation
}
deffenseRotation=
{
cardSlotDefenceRotation
()
}
clearPlaceInteractivitiesAction=
{
clearMonsterPlaceInteractivities
}
/>
)
:
(
<></>
)
}
{
meRight
?
(
<
FixedSlot
state=
{
meRight
}
sequence=
{
6
}
position=
{
rightPosition
}
rotation=
{
meRotation
}
deffenseRotation=
{
cardSlotDefenceRotation
()
}
clearPlaceInteractivitiesAction=
{
clearMonsterPlaceInteractivities
}
/>
)
:
(
<></>
)
}
{
opLeft
?
(
<
FixedSlot
state=
{
opLeft
}
sequence=
{
5
}
position=
{
rightPosition
}
rotation=
{
opRotation
}
deffenseRotation=
{
cardSlotDefenceRotation
()
}
clearPlaceInteractivitiesAction=
{
clearMonsterPlaceInteractivities
}
/>
)
:
(
<></>
)
}
{
opRight
?
(
<
FixedSlot
state=
{
opRight
}
sequence=
{
6
}
position=
{
leftPosition
}
rotation=
{
opRotation
}
deffenseRotation=
{
cardSlotDefenceRotation
()
}
clearPlaceInteractivitiesAction=
{
clearMonsterPlaceInteractivities
}
/>
)
:
(
<></>
)
}
</>
);
};
const
monsterPositions
=
(
player
:
number
,
monsters
:
CardState
[])
=>
{
const
x
=
(
sequence
:
number
)
=>
player
==
0
?
left
+
gap
*
sequence
:
-
left
-
gap
*
sequence
;
const
y
=
transform
.
z
/
2
+
floating
;
const
z
=
player
==
0
?
-
1.35
:
1.35
;
return
monsters
.
map
((
_
,
sequence
)
=>
new
BABYLON
.
Vector3
(
x
(
sequence
),
y
,
z
));
};
export
default
Monsters
;
src/ui/Duel/PlayMat/SingleSlot.tsx
0 → 100644
View file @
db603083
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
{
CardState
}
from
"
@/reducers/duel/generic
"
;
import
{
store
}
from
"
@/store
"
;
import
{
useClick
}
from
"
@/hook
"
;
import
{
useRef
}
from
"
react
"
;
import
{
setCardListModalInfo
,
setCardListModalIsOpen
,
}
from
"
@/reducers/duel/mod
"
;
import
{
interactTypeToString
}
from
"
../utils
"
;
import
NeosConfig
from
"
../../../../neos.config.json
"
;
const
transform
=
NeosConfig
.
ui
.
card
.
transform
;
export
const
Depth
=
0.005
;
const
SingleSlot
=
(
props
:
{
state
:
CardState
[];
position
:
BABYLON
.
Vector3
;
rotation
:
BABYLON
.
Vector3
;
})
=>
{
const
boxRef
=
useRef
(
null
);
const
dispatch
=
store
.
dispatch
;
const
edgeRender
=
props
.
state
.
find
((
item
)
=>
item
===
undefined
?
false
:
item
.
idleInteractivities
.
length
>
0
)
!==
undefined
;
const
edgesWidth
=
2.0
;
const
edgesColor
=
BABYLON
.
Color4
.
FromColor3
(
BABYLON
.
Color3
.
Yellow
());
useClick
(
(
_event
)
=>
{
if
(
props
.
state
.
length
!=
0
)
{
dispatch
(
setCardListModalInfo
(
props
.
state
.
filter
(
(
item
)
=>
item
.
occupant
!==
undefined
&&
item
.
occupant
.
id
!==
0
)
.
map
((
item
)
=>
{
return
{
meta
:
item
.
occupant
,
interactivies
:
item
.
idleInteractivities
.
map
((
interactivy
)
=>
{
return
{
desc
:
interactTypeToString
(
interactivy
.
interactType
),
response
:
interactivy
.
response
,
};
}),
};
})
)
);
dispatch
(
setCardListModalIsOpen
(
true
));
}
},
boxRef
,
[
props
.
state
]
);
return
(
<
box
name=
"single-slot"
ref=
{
boxRef
}
scaling=
{
new
BABYLON
.
Vector3
(
transform
.
x
,
transform
.
y
,
Depth
*
props
.
state
.
length
)
}
position=
{
props
.
position
}
rotation=
{
props
.
rotation
}
enableEdgesRendering
edgesWidth=
{
edgeRender
?
edgesWidth
:
0
}
edgesColor=
{
edgesColor
}
>
<
standardMaterial
name=
"single-slot-mat"
diffuseTexture=
{
new
BABYLON
.
Texture
(
`${NeosConfig.assetsPath}/card_back.jpg`
)
}
alpha=
{
props
.
state
.
length
==
0
?
0
:
1
}
/>
</
box
>
);
};
export
default
SingleSlot
;
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