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
65b053c2
Commit
65b053c2
authored
Apr 28, 2024
by
Chunchi Che
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'fix/announce_card' into 'main'
修复宣言卡片的逻辑处理 See merge request
mycard/Neos!365
parents
a7340d82
88b5a209
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
324 additions
and
27 deletions
+324
-27
src/common.ts
src/common.ts
+162
-0
src/middleware/sqlite/fts.ts
src/middleware/sqlite/fts.ts
+11
-0
src/service/duel/announce.ts
src/service/duel/announce.ts
+3
-12
src/service/utils/genCard.ts
src/service/utils/genCard.ts
+2
-1
src/ui/BuildDeck/index.tsx
src/ui/BuildDeck/index.tsx
+1
-10
src/ui/Duel/Main.tsx
src/ui/Duel/Main.tsx
+2
-0
src/ui/Duel/Message/AnnounceModal/index.module.scss
src/ui/Duel/Message/AnnounceModal/index.module.scss
+8
-0
src/ui/Duel/Message/AnnounceModal/index.tsx
src/ui/Duel/Message/AnnounceModal/index.tsx
+130
-0
src/ui/Duel/Message/CardListModal/index.tsx
src/ui/Duel/Message/CardListModal/index.tsx
+1
-1
src/ui/Duel/Message/OptionModal/index.tsx
src/ui/Duel/Message/OptionModal/index.tsx
+1
-1
src/ui/Duel/Message/PositionModal/index.tsx
src/ui/Duel/Message/PositionModal/index.tsx
+1
-1
src/ui/Duel/Message/SortCardModal/index.tsx
src/ui/Duel/Message/SortCardModal/index.tsx
+1
-1
src/ui/Shared/YgoCard/index.tsx
src/ui/Shared/YgoCard/index.tsx
+1
-0
No files found.
src/common.ts
View file @
65b053c2
import
{
ygopro
}
from
"
@/api
"
;
import
PhaseType
=
ygopro
.
StocGameMessage
.
MsgNewPhase
.
PhaseType
;
import
{
CardMeta
}
from
"
@/api
"
;
//! 一些Neos中基础的数据结构
// 类型
...
...
@@ -284,3 +285,164 @@ export const Phase2StringCodeMap: Map<number, number> = new Map([
[
PhaseType
.
MAIN2
,
22
],
[
PhaseType
.
END
,
26
],
]);
// For Announce Card
const
OPCODE_ADD
=
0x40000000
;
const
OPCODE_SUB
=
0x40000001
;
const
OPCODE_MUL
=
0x40000002
;
const
OPCODE_DIV
=
0x40000003
;
const
OPCODE_AND
=
0x40000004
;
const
OPCODE_OR
=
0x40000005
;
const
OPCODE_NEG
=
0x40000006
;
const
OPCODE_NOT
=
0x40000007
;
const
OPCODE_ISCODE
=
0x40000100
;
const
OPCODE_ISSETCARD
=
0x40000101
;
const
OPCODE_ISTYPE
=
0x40000102
;
const
OPCODE_ISRACE
=
0x40000103
;
const
OPCODE_ISATTRIBUTE
=
0x40000104
;
const
CARD_MARINE_DOLPHIN
=
78734254
;
const
CARD_TWINKLE_MOSS
=
13857930
;
/*
* 判断一张卡是否能被宣言
* 用于处理`AnnounceCard`
* */
export
function
isDeclarable
(
card
:
CardMeta
,
opcodes
:
number
[]):
boolean
{
const
stack
:
number
[]
=
[];
for
(
const
opcode
of
opcodes
)
{
switch
(
opcode
)
{
case
OPCODE_ADD
:
{
if
(
stack
.
length
>=
2
)
{
const
rhs
=
stack
.
pop
()
!
;
const
lhs
=
stack
.
pop
()
!
;
stack
.
push
(
lhs
+
rhs
);
}
break
;
}
case
OPCODE_SUB
:
{
if
(
stack
.
length
>=
2
)
{
const
rhs
=
stack
.
pop
()
!
;
const
lhs
=
stack
.
pop
()
!
;
stack
.
push
(
lhs
-
rhs
);
}
break
;
}
case
OPCODE_MUL
:
{
if
(
stack
.
length
>=
2
)
{
const
rhs
=
stack
.
pop
()
!
;
const
lhs
=
stack
.
pop
()
!
;
stack
.
push
(
lhs
*
rhs
);
}
break
;
}
case
OPCODE_DIV
:
{
if
(
stack
.
length
>=
2
)
{
const
rhs
=
stack
.
pop
()
!
;
const
lhs
=
stack
.
pop
()
!
;
stack
.
push
(
lhs
/
rhs
);
}
break
;
}
case
OPCODE_AND
:
{
if
(
stack
.
length
>=
2
)
{
const
rhs
=
stack
.
pop
()
!
;
const
lhs
=
stack
.
pop
()
!
;
const
b0
=
rhs
!==
0
;
const
b1
=
lhs
!==
0
;
stack
.
push
(
Number
(
b0
&&
b1
));
}
break
;
}
case
OPCODE_OR
:
{
if
(
stack
.
length
>=
2
)
{
const
rhs
=
stack
.
pop
()
!
;
const
lhs
=
stack
.
pop
()
!
;
const
b0
=
rhs
!==
0
;
const
b1
=
lhs
!==
0
;
stack
.
push
(
Number
(
b0
||
b1
));
}
break
;
}
case
OPCODE_NEG
:
{
if
(
stack
.
length
>=
1
)
{
const
rhs
=
stack
.
pop
()
!
;
stack
.
push
(
-
rhs
);
}
break
;
}
case
OPCODE_NOT
:
{
if
(
stack
.
length
>=
1
)
{
const
rhs
=
stack
.
pop
()
!
;
stack
.
push
(
Number
(
rhs
===
0
));
}
break
;
}
case
OPCODE_ISCODE
:
{
if
(
stack
.
length
>=
1
)
{
const
code
=
stack
.
pop
()
!
;
stack
.
push
(
Number
(
code
===
card
.
id
));
}
break
;
}
case
OPCODE_ISSETCARD
:
{
if
(
stack
.
length
>=
1
)
{
const
setCode
=
stack
.
pop
()
!
;
stack
.
push
(
Number
(
ifSetCard
(
setCode
,
card
.
data
.
setcode
??
0
)));
}
break
;
}
case
OPCODE_ISTYPE
:
{
if
(
stack
.
length
>=
1
)
{
const
type_
=
stack
.
pop
()
!
;
stack
.
push
(
Number
((
type_
&
(
card
.
data
.
type
??
0
))
>
0
));
}
break
;
}
case
OPCODE_ISRACE
:
{
if
(
stack
.
length
>=
1
)
{
const
race_
=
stack
.
pop
()
!
;
stack
.
push
(
Number
((
race_
&
(
card
.
data
.
race
??
0
))
>
0
));
}
break
;
}
case
OPCODE_ISATTRIBUTE
:
{
if
(
stack
.
length
>=
1
)
{
const
attribute_
=
stack
.
pop
()
!
;
stack
.
push
(
Number
((
attribute_
&
(
card
.
data
.
attribute
??
0
))
>
0
));
}
break
;
}
default
:
{
stack
.
push
(
opcode
);
break
;
}
}
}
if
(
stack
.
length
!==
1
||
stack
.
pop
()
===
0
)
return
false
;
return
(
card
.
id
===
CARD_MARINE_DOLPHIN
||
card
.
id
===
CARD_TWINKLE_MOSS
||
(
!
(
card
.
data
.
alias
!==
0
)
&&
(
card
.
data
.
type
??
0
&
(
TYPE_MONSTER
+
TYPE_TOKEN
))
!==
TYPE_MONSTER
+
TYPE_TOKEN
)
);
}
function
ifSetCard
(
setCodeToAnalyse
:
number
,
setCodeFromCard
:
number
):
boolean
{
let
res
=
false
;
const
settype
=
setCodeToAnalyse
&
0xfff
;
const
setsubtype
=
setCodeToAnalyse
&
0xf000
;
let
sc
=
setCodeFromCard
;
while
(
sc
!==
0
)
{
if
((
sc
&
0xfff
)
===
settype
&&
(
sc
&
0xf000
&
setsubtype
)
===
setsubtype
)
res
=
true
;
sc
=
sc
>>
16
;
}
return
res
;
}
src/middleware/sqlite/fts.ts
View file @
65b053c2
...
...
@@ -16,6 +16,17 @@ export interface FtsConditions {
atk
:
{
min
:
number
|
null
;
max
:
number
|
null
};
// 攻击力区间
def
:
{
min
:
number
|
null
;
max
:
number
|
null
};
// 防御力区间
}
export
const
emptySearchConditions
:
FtsConditions
=
{
atk
:
{
min
:
null
,
max
:
null
},
def
:
{
min
:
null
,
max
:
null
},
levels
:
[],
lscales
:
[],
races
:
[],
attributes
:
[],
types
:
[],
};
export
interface
FtsParams
{
query
:
string
;
// 用于全文检索的query
conditions
:
FtsConditions
;
// 过滤条件
...
...
src/service/duel/announce.ts
View file @
65b053c2
import
{
fetch
Card
,
fetch
Strings
,
Region
,
ygopro
}
from
"
@/api
"
;
import
{
fetchStrings
,
Region
,
ygopro
}
from
"
@/api
"
;
import
{
displayOptionModal
}
from
"
@/ui/Duel/Message
"
;
import
MsgAnnounce
=
ygopro
.
StocGameMessage
.
MsgAnnounce
;
import
{
displayAnnounceModal
}
from
"
@/ui/Duel/Message/AnnounceModal
"
;
export
default
async
(
announce
:
MsgAnnounce
)
=>
{
const
type_
=
announce
.
announce_type
;
...
...
@@ -38,17 +39,7 @@ export default async (announce: MsgAnnounce) => {
break
;
}
case
MsgAnnounce
.
AnnounceType
.
Card
:
{
const
options
=
[];
for
(
const
option
of
announce
.
options
)
{
const
meta
=
fetchCard
(
option
.
code
);
if
(
meta
.
text
.
name
)
{
options
.
push
({
info
:
meta
.
text
.
name
,
response
:
option
.
response
,
});
}
}
await
displayOptionModal
(
fetchStrings
(
Region
.
System
,
564
),
options
,
min
);
await
displayAnnounceModal
(
announce
.
options
.
map
((
option
)
=>
option
.
code
));
break
;
}
...
...
src/service/utils/genCard.ts
View file @
65b053c2
...
...
@@ -6,7 +6,8 @@ import { CardType } from "@/stores";
// 自动从code推断出meta
//
// TODO: 其实不是很推荐这样做,因为随着项目复杂度增加,这样可能会带来meta更新的时序问题
// TODO: 其实不是很推荐这样做,因为随着项目复杂度增加,
// 这样可能会带来meta更新的时序问题
export
const
genCard
=
(
card
:
CardType
)
=>
{
const
t
=
proxy
(
card
);
subscribeKey
(
t
,
"
code
"
,
async
(
code
)
=>
{
...
...
src/ui/BuildDeck/index.tsx
View file @
65b053c2
...
...
@@ -33,7 +33,7 @@ import { subscribeKey } from "valtio/utils";
import
{
type
CardMeta
,
searchCards
}
from
"
@/api
"
;
import
{
isExtraDeckCard
,
isToken
}
from
"
@/common
"
;
import
{
FtsConditions
}
from
"
@/middleware/sqlite/fts
"
;
import
{
emptySearchConditions
,
FtsConditions
}
from
"
@/middleware/sqlite/fts
"
;
import
{
deckStore
,
emptyDeck
,
type
IDeck
,
initStore
}
from
"
@/stores
"
;
import
{
Background
,
...
...
@@ -327,15 +327,6 @@ export const DeckEditor: React.FC<{
const
Search
:
React
.
FC
=
()
=>
{
const
{
modal
}
=
App
.
useApp
();
const
[
searchWord
,
setSearchWord
]
=
useState
(
""
);
const
emptySearchConditions
:
FtsConditions
=
{
atk
:
{
min
:
null
,
max
:
null
},
def
:
{
min
:
null
,
max
:
null
},
levels
:
[],
lscales
:
[],
races
:
[],
attributes
:
[],
types
:
[],
};
const
[
searchConditions
,
setSearchConditions
]
=
useState
<
FtsConditions
>
(
emptySearchConditions
,
);
...
...
src/ui/Duel/Main.tsx
View file @
65b053c2
...
...
@@ -18,6 +18,7 @@ import {
SortCardModal
,
YesNoModal
,
}
from
"
./Message
"
;
import
{
AnnounceModal
}
from
"
./Message/AnnounceModal
"
;
import
{
LifeBar
,
Mat
,
Menu
,
Underlying
}
from
"
./PlayMat
"
;
import
{
ChatBox
}
from
"
./PlayMat/ChatBox
"
;
import
{
HandChain
}
from
"
./PlayMat/HandChain
"
;
...
...
@@ -58,6 +59,7 @@ export const Component: React.FC = () => {
<
CheckCounterModal
/>
<
SortCardModal
/>
<
SimpleSelectCardsModal
/>
<
AnnounceModal
/>
<
EndModal
/>
<
ChatBox
/>
<
HandChain
/>
...
...
src/ui/Duel/Message/AnnounceModal/index.module.scss
0 → 100644
View file @
65b053c2
.container
{
display
:
flex
;
flex-direction
:
column
;
.input
{
display
:
flex
;
}
}
src/ui/Duel/Message/AnnounceModal/index.tsx
0 → 100644
View file @
65b053c2
import
{
SearchOutlined
}
from
"
@ant-design/icons
"
;
import
{
Avatar
,
Button
,
Checkbox
,
Input
,
List
}
from
"
antd
"
;
import
React
,
{
useState
}
from
"
react
"
;
import
{
proxy
,
useSnapshot
}
from
"
valtio
"
;
import
{
CardMeta
,
searchCards
,
sendSelectOptionResponse
}
from
"
@/api
"
;
import
{
isDeclarable
,
isToken
}
from
"
@/common
"
;
import
{
emptySearchConditions
}
from
"
@/middleware/sqlite/fts
"
;
import
{
getCardImgUrl
}
from
"
@/ui/Shared
"
;
import
{
NeosModal
}
from
"
../NeosModal
"
;
import
styles
from
"
./index.module.scss
"
;
const
MAX_DESC_LEN
=
20
;
const
PAGE_SIZE
=
5
;
interface
Props
{
isOpen
:
boolean
;
opcodes
:
number
[];
}
const
defaultProps
=
{
isOpen
:
false
,
opcodes
:
[],
};
const
store
=
proxy
<
Props
>
(
defaultProps
);
export
const
AnnounceModal
:
React
.
FC
=
()
=>
{
const
{
isOpen
}
=
useSnapshot
(
store
);
const
[
searchWord
,
setSearchWord
]
=
useState
(
""
);
const
[
cardList
,
setCardList
]
=
useState
<
CardMeta
[]
>
([]);
const
[
selected
,
setSelected
]
=
useState
<
number
|
undefined
>
(
undefined
);
const
handleSearch
=
()
=>
{
const
result
=
searchCards
({
query
:
searchWord
,
conditions
:
emptySearchConditions
,
}).
filter
(
(
card
)
=>
isDeclarable
(
card
,
store
.
opcodes
)
&&
!
isToken
(
card
.
data
.
type
??
0
),
);
// 清掉之前选中的记录
setSelected
(
undefined
);
setCardList
(
result
);
};
const
onSummit
=
()
=>
{
if
(
selected
!==
undefined
)
{
sendSelectOptionResponse
(
selected
);
rs
();
setSearchWord
(
""
);
setCardList
([]);
}
};
return
(
<
NeosModal
title=
"请输入关键字并选择宣言的卡"
open=
{
isOpen
}
footer=
{
<
Button
disabled=
{
selected
===
undefined
}
onClick=
{
onSummit
}
>
确定
</
Button
>
}
>
<
div
className=
{
styles
.
container
}
>
<
Input
className=
{
styles
.
input
}
placeholder=
"请输入宣言卡名(或关键字)"
bordered=
{
false
}
value=
{
searchWord
}
onChange=
{
(
e
)
=>
setSearchWord
(
e
.
target
.
value
)
}
suffix=
{
<
Button
type=
"text"
icon=
{
<
SearchOutlined
/>
}
onClick=
{
()
=>
handleSearch
()
}
/>
}
onKeyUp=
{
(
e
)
=>
e
.
key
===
"
Enter
"
&&
handleSearch
()
}
allowClear
/>
<
List
pagination=
{
{
position
:
"
bottom
"
,
align
:
"
center
"
,
pageSize
:
PAGE_SIZE
,
}
}
dataSource=
{
cardList
}
renderItem=
{
(
item
,
index
)
=>
(
<
List
.
Item
key=
{
index
}
actions=
{
[
<
Checkbox
checked=
{
item
.
id
===
selected
}
onClick=
{
()
=>
{
if
(
item
.
id
===
selected
)
{
// 之前选的就是这个,则取消选中
setSelected
(
undefined
);
}
else
{
// 选中
setSelected
(
item
.
id
);
}
}
}
/>,
]
}
>
<
List
.
Item
.
Meta
avatar=
{
<
Avatar
src=
{
getCardImgUrl
(
item
.
id
)
}
/>
}
title=
{
<
a
>
{
item
.
text
.
name
}
</
a
>
}
description=
{
item
.
text
.
desc
?.
substring
(
0
,
MAX_DESC_LEN
)
+
"
...
"
}
/>
</
List
.
Item
>
)
}
/>
</
div
>
</
NeosModal
>
);
};
let
rs
:
(
v
?:
any
)
=>
void
=
()
=>
{};
export
const
displayAnnounceModal
=
async
(
opcodes
:
number
[])
=>
{
store
.
opcodes
=
opcodes
;
store
.
isOpen
=
true
;
await
new
Promise
((
resolve
)
=>
(
rs
=
resolve
));
store
.
isOpen
=
false
;
store
.
opcodes
=
[];
};
src/ui/Duel/Message/CardListModal.tsx
→
src/ui/Duel/Message/CardListModal
/index
.tsx
View file @
65b053c2
...
...
@@ -6,7 +6,7 @@ import { ygopro } from "@/api";
import
{
cardStore
,
CardType
}
from
"
@/stores
"
;
import
{
YgoCard
}
from
"
@/ui/Shared
"
;
import
{
showCardModal
}
from
"
./CardModal
"
;
import
{
showCardModal
}
from
"
.
.
/CardModal
"
;
const
CARD_WIDTH
=
"
6.25rem
"
;
const
DRAWER_WIDTH
=
"
10rem
"
;
...
...
src/ui/Duel/Message/OptionModal.tsx
→
src/ui/Duel/Message/OptionModal
/index
.tsx
View file @
65b053c2
...
...
@@ -13,7 +13,7 @@ import {
sendSelectOptionResponse
,
}
from
"
@/api
"
;
import
{
NeosModal
}
from
"
./NeosModal
"
;
import
{
NeosModal
}
from
"
.
.
/NeosModal
"
;
type
Options
=
{
info
:
string
;
response
:
number
}[];
...
...
src/ui/Duel/Message/PositionModal.tsx
→
src/ui/Duel/Message/PositionModal
/index
.tsx
View file @
65b053c2
...
...
@@ -6,7 +6,7 @@ import { proxy, useSnapshot } from "valtio";
import
{
sendSelectPositionResponse
,
ygopro
}
from
"
@/api
"
;
import
{
NeosModal
}
from
"
./NeosModal
"
;
import
{
NeosModal
}
from
"
.
.
/NeosModal
"
;
interface
PositionModalProps
{
isOpen
:
boolean
;
...
...
src/ui/Duel/Message/SortCardModal.tsx
→
src/ui/Duel/Message/SortCardModal
/index
.tsx
View file @
65b053c2
...
...
@@ -24,7 +24,7 @@ import { sendSortCardResponse } from "@/api";
import
{
CardMeta
}
from
"
@/api/cards
"
;
import
{
getCardImgUrl
}
from
"
@/ui/Shared
"
;
import
{
NeosModal
}
from
"
./NeosModal
"
;
import
{
NeosModal
}
from
"
.
.
/NeosModal
"
;
interface
SortOption
{
meta
:
CardMeta
;
...
...
src/ui/Shared/YgoCard/index.tsx
View file @
65b053c2
...
...
@@ -53,6 +53,7 @@ export const YgoCard: React.FC<Props> = (props) => {
const
NeosConfig
=
useConfig
();
// TODO: 这个函数应该从这个文件抽离出来作为公共的函数使用
export
function
getCardImgUrl
(
code
:
number
,
back
=
false
)
{
const
ASSETS_BASE
=
import
.
meta
.
env
.
BASE_URL
===
"
/
"
...
...
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