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
90419ce0
Commit
90419ce0
authored
Jan 12, 2023
by
Chunchi Che
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feat/reducers' into 'main'
Feat/reducers See merge request
mycard/Neos!71
parents
9c3989e3
b33802fc
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
378 additions
and
310 deletions
+378
-310
src/config/ui.ts
src/config/ui.ts
+1
-0
src/reducers/duel/cemeretySlice.ts
src/reducers/duel/cemeretySlice.ts
+16
-49
src/reducers/duel/exclusionSlice.ts
src/reducers/duel/exclusionSlice.ts
+75
-0
src/reducers/duel/generic.ts
src/reducers/duel/generic.ts
+111
-0
src/reducers/duel/handsSlice.ts
src/reducers/duel/handsSlice.ts
+12
-15
src/reducers/duel/magicSlice.ts
src/reducers/duel/magicSlice.ts
+18
-79
src/reducers/duel/mod.ts
src/reducers/duel/mod.ts
+12
-0
src/reducers/duel/monstersSlice.ts
src/reducers/duel/monstersSlice.ts
+23
-78
src/ui/Duel/cemetery.tsx
src/ui/Duel/cemetery.tsx
+5
-67
src/ui/Duel/exclusion.tsx
src/ui/Duel/exclusion.tsx
+30
-16
src/ui/Duel/hands.tsx
src/ui/Duel/hands.tsx
+2
-2
src/ui/Duel/magics.tsx
src/ui/Duel/magics.tsx
+2
-2
src/ui/Duel/monsters.tsx
src/ui/Duel/monsters.tsx
+2
-2
src/ui/Duel/singleSlot.tsx
src/ui/Duel/singleSlot.tsx
+69
-0
No files found.
src/config/ui.ts
View file @
90419ce0
...
...
@@ -17,6 +17,7 @@ export const DeckSlotShape = () => {
export
const
ExtraDeckSlotShape
=
()
=>
{
return
{
width
:
0.8
,
height
:
1
,
depth
:
0.2
};
};
export
const
SingleSlotShape
=
{
width
:
0.8
,
height
:
1
,
depth
:
0.2
};
export
const
CemeterySlotShape
=
()
=>
{
return
{
width
:
0.8
,
height
:
1
,
depth
:
0.2
};
};
...
...
src/reducers/duel/cemeretySlice.ts
View file @
90419ce0
...
...
@@ -2,18 +2,19 @@ import { judgeSelf } from "./util";
import
{
PayloadAction
,
CaseReducer
,
createAsyncThunk
,
ActionReducerMapBuilder
,
}
from
"
@reduxjs/toolkit
"
;
import
{
DuelState
}
from
"
./mod
"
;
import
{
RootState
}
from
"
../../store
"
;
import
{
fetchCard
}
from
"
../../api/cards
"
;
import
{
CardState
}
from
"
./generic
"
;
import
{
ygopro
}
from
"
../../api/ocgcore/idl/ocgcore
"
;
import
{
createAsyncMetaThunk
,
DuelFieldState
,
extendState
,
extendMeta
,
}
from
"
./generic
"
;
export
interface
CemeteryState
{
cemetery
:
CardState
[];
}
export
interface
CemeteryState
extends
DuelFieldState
{}
// 初始化墓地状态
export
const
initCemeteryImpl
:
CaseReducer
<
DuelState
,
PayloadAction
<
number
>>
=
(
...
...
@@ -22,28 +23,14 @@ export const initCemeteryImpl: CaseReducer<DuelState, PayloadAction<number>> = (
)
=>
{
const
player
=
action
.
payload
;
if
(
judgeSelf
(
player
,
state
))
{
state
.
meCemetery
=
{
cemetery
:
[]
};
state
.
meCemetery
=
{
inner
:
[]
};
}
else
{
state
.
opCemetery
=
{
cemetery
:
[]
};
state
.
opCemetery
=
{
inner
:
[]
};
}
};
// 增加墓地
export
const
fetchCemeteryMeta
=
createAsyncThunk
(
"
duel/fetchCemeteryMeta
"
,
async
(
param
:
{
controler
:
number
;
sequence
:
number
;
code
:
number
})
=>
{
const
code
=
param
.
code
;
const
meta
=
await
fetchCard
(
code
);
const
response
=
{
controler
:
param
.
controler
,
sequence
:
param
.
sequence
,
meta
,
};
return
response
;
}
);
export
const
fetchCemeteryMeta
=
createAsyncMetaThunk
(
"
duel/fetchCemeteryMeta
"
);
export
const
cemeteryCase
=
(
builder
:
ActionReducerMapBuilder
<
DuelState
>
)
=>
{
builder
.
addCase
(
fetchCemeteryMeta
.
pending
,
(
state
,
action
)
=>
{
...
...
@@ -62,17 +49,9 @@ export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => {
idleInteractivities
:
[],
};
if
(
judgeSelf
(
controler
,
state
))
{
if
(
state
.
meCemetery
)
{
state
.
meCemetery
.
cemetery
.
push
(
newCemetery
);
}
else
{
state
.
meCemetery
=
{
cemetery
:
[
newCemetery
]
};
}
extendState
(
state
.
meCemetery
,
newCemetery
);
}
else
{
if
(
state
.
opCemetery
)
{
state
.
opCemetery
.
cemetery
.
push
(
newCemetery
);
}
else
{
state
.
opCemetery
=
{
cemetery
:
[
newCemetery
]
};
}
extendState
(
state
.
opCemetery
,
newCemetery
);
}
});
builder
.
addCase
(
fetchCemeteryMeta
.
fulfilled
,
(
state
,
action
)
=>
{
...
...
@@ -81,26 +60,14 @@ export const cemeteryCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const
meta
=
action
.
payload
.
meta
;
if
(
judgeSelf
(
controler
,
state
))
{
if
(
state
.
meCemetery
)
{
for
(
const
cemetery
of
state
.
meCemetery
.
cemetery
)
{
if
(
cemetery
.
location
.
sequence
==
sequence
)
{
cemetery
.
occupant
=
meta
;
}
}
}
extendMeta
(
state
.
meCemetery
,
meta
,
sequence
);
}
else
{
if
(
state
.
opCemetery
)
{
for
(
const
cemetery
of
state
.
opCemetery
.
cemetery
)
{
if
(
cemetery
.
location
.
sequence
==
sequence
)
{
cemetery
.
occupant
=
meta
;
}
}
}
extendMeta
(
state
.
opCemetery
,
meta
,
sequence
);
}
});
};
export
const
selectMeCemetery
=
(
state
:
RootState
)
=>
state
.
duel
.
meCemetery
||
{
cemetery
:
[]
};
state
.
duel
.
meCemetery
||
{
inner
:
[]
};
export
const
selectOpCemetery
=
(
state
:
RootState
)
=>
state
.
duel
.
opCemetery
||
{
cemetery
:
[]
};
state
.
duel
.
opCemetery
||
{
inner
:
[]
};
src/reducers/duel/exclusionSlice.ts
0 → 100644
View file @
90419ce0
import
{
judgeSelf
}
from
"
./util
"
;
import
{
PayloadAction
,
CaseReducer
,
ActionReducerMapBuilder
,
}
from
"
@reduxjs/toolkit
"
;
import
{
DuelState
}
from
"
./mod
"
;
import
{
RootState
}
from
"
../../store
"
;
import
{
ygopro
}
from
"
../../api/ocgcore/idl/ocgcore
"
;
import
{
createAsyncMetaThunk
,
DuelFieldState
,
extendState
,
extendMeta
,
}
from
"
./generic
"
;
export
interface
ExclusionState
extends
DuelFieldState
{}
// 初始化除外区状态
export
const
initExclusionImpl
:
CaseReducer
<
DuelState
,
PayloadAction
<
number
>
>
=
(
state
,
action
)
=>
{
const
player
=
action
.
payload
;
if
(
judgeSelf
(
player
,
state
))
{
state
.
meExclusion
=
{
inner
:
[]
};
}
else
{
state
.
opExclusion
=
{
inner
:
[]
};
}
};
// 增加除外区
export
const
fetchExclusionMeta
=
createAsyncMetaThunk
(
"
duel/fetchExclusionMeta
"
);
export
const
exclusionCase
=
(
builder
:
ActionReducerMapBuilder
<
DuelState
>
)
=>
{
builder
.
addCase
(
fetchExclusionMeta
.
pending
,
(
state
,
action
)
=>
{
// Meta结果没返回之前先更新`ID`
const
controler
=
action
.
meta
.
arg
.
controler
;
const
sequence
=
action
.
meta
.
arg
.
sequence
;
const
code
=
action
.
meta
.
arg
.
code
;
const
newExclusion
=
{
occupant
:
{
id
:
code
,
data
:
{},
text
:
{}
},
location
:
{
controler
,
location
:
ygopro
.
CardZone
.
REMOVED
,
sequence
,
},
idleInteractivities
:
[],
};
if
(
judgeSelf
(
controler
,
state
))
{
extendState
(
state
.
meExclusion
,
newExclusion
);
}
else
{
extendState
(
state
.
opExclusion
,
newExclusion
);
}
});
builder
.
addCase
(
fetchExclusionMeta
.
fulfilled
,
(
state
,
action
)
=>
{
const
controler
=
action
.
payload
.
controler
;
const
sequence
=
action
.
payload
.
sequence
;
const
meta
=
action
.
payload
.
meta
;
if
(
judgeSelf
(
controler
,
state
))
{
extendMeta
(
state
.
meExclusion
,
meta
,
sequence
);
}
else
{
extendMeta
(
state
.
opExclusion
,
meta
,
sequence
);
}
});
};
export
const
selectMeExclusion
=
(
state
:
RootState
)
=>
state
.
duel
.
meExclusion
||
{
inner
:
[]
};
export
const
selectopExclusion
=
(
state
:
RootState
)
=>
state
.
duel
.
opExclusion
||
{
inner
:
[]
};
src/reducers/duel/generic.ts
View file @
90419ce0
import
{
AsyncThunk
,
createAsyncThunk
}
from
"
@reduxjs/toolkit
"
;
import
{
CardMeta
}
from
"
../../api/cards
"
;
import
{
ygopro
}
from
"
../../api/ocgcore/idl/ocgcore
"
;
import
{
fetchCard
}
from
"
../../api/cards
"
;
export
interface
DuelFieldState
{
inner
:
CardState
[];
}
export
interface
CardState
{
occupant
?:
CardMeta
;
// 占据此位置的卡牌元信息
...
...
@@ -42,3 +48,108 @@ export interface Interactivity<T> {
// 用户点击后,需要回传给服务端的`response`
response
:
T
;
}
export
function
createAsyncMetaThunk
(
name
:
string
):
AsyncThunk
<
{
controler
:
number
;
sequence
:
number
;
meta
:
CardMeta
},
{
controler
:
number
;
sequence
:
number
;
position
?:
ygopro
.
CardPosition
;
code
:
number
;
},
{}
>
{
return
createAsyncThunk
(
name
,
async
(
param
:
{
controler
:
number
;
sequence
:
number
;
position
?:
ygopro
.
CardPosition
;
code
:
number
;
})
=>
{
const
code
=
param
.
code
;
const
meta
=
await
fetchCard
(
code
);
const
response
=
{
controler
:
param
.
controler
,
sequence
:
param
.
sequence
,
meta
,
};
return
response
;
}
);
}
export
function
extendState
<
T
extends
DuelFieldState
>
(
state
:
T
|
undefined
,
newState
:
CardState
)
{
if
(
state
)
{
state
.
inner
.
push
(
newState
);
}
}
export
function
extendOccupant
<
T
extends
DuelFieldState
>
(
state
:
T
|
undefined
,
newMeta
:
CardMeta
,
sequence
:
number
,
position
?:
ygopro
.
CardPosition
)
{
if
(
state
)
{
for
(
const
item
of
state
.
inner
)
{
if
(
item
.
location
.
sequence
==
sequence
)
{
item
.
occupant
=
newMeta
;
if
(
position
)
{
item
.
location
.
position
=
position
;
}
}
}
}
}
export
function
extendMeta
<
T
extends
DuelFieldState
>
(
state
:
T
|
undefined
,
newMeta
:
CardMeta
,
sequence
:
number
)
{
if
(
state
)
{
for
(
const
item
of
state
.
inner
)
{
if
(
item
.
location
.
sequence
==
sequence
)
{
item
.
occupant
=
newMeta
;
}
}
}
}
export
function
extendPlaceInteractivity
<
T
extends
DuelFieldState
>
(
state
:
T
|
undefined
,
controler
:
number
,
sequence
:
number
,
zone
:
ygopro
.
CardZone
)
{
if
(
state
)
{
for
(
let
item
of
state
.
inner
)
{
if
(
item
.
location
.
sequence
==
sequence
)
{
item
.
placeInteractivities
=
{
interactType
:
InteractType
.
PLACE_SELECTABLE
,
response
:
{
controler
,
zone
,
sequence
,
},
};
}
}
}
}
export
function
clearPlaceInteractivities
<
T
extends
DuelFieldState
>
(
state
:
T
|
undefined
)
{
if
(
state
)
{
for
(
let
item
of
state
.
inner
)
{
item
.
placeInteractivities
=
undefined
;
}
}
}
src/reducers/duel/handsSlice.ts
View file @
90419ce0
...
...
@@ -8,13 +8,10 @@ import { DuelState } from "./mod";
import
{
RootState
}
from
"
../../store
"
;
import
{
fetchCard
,
CardMeta
}
from
"
../../api/cards
"
;
import
{
judgeSelf
}
from
"
./util
"
;
import
{
CardState
,
Interactivity
}
from
"
./generic
"
;
import
{
Interactivity
,
DuelFieldState
}
from
"
./generic
"
;
import
{
ygopro
}
from
"
../../api/ocgcore/idl/ocgcore
"
;
export
interface
HandState
{
// 注意:手牌的位置顺序是有约束的
hands
:
CardState
[];
}
export
interface
HandState
extends
DuelFieldState
{}
// 增加手牌
export
const
fetchHandsMeta
=
createAsyncThunk
(
...
...
@@ -57,15 +54,15 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
});
if
(
judgeSelf
(
player
,
state
))
{
if
(
state
.
meHands
)
{
state
.
meHands
.
hands
=
state
.
meHands
.
hands
.
concat
(
cards
);
state
.
meHands
.
inner
=
state
.
meHands
.
inner
.
concat
(
cards
);
}
else
{
state
.
meHands
=
{
hands
:
cards
};
state
.
meHands
=
{
inner
:
cards
};
}
}
else
{
if
(
state
.
opHands
)
{
state
.
opHands
.
hands
=
state
.
opHands
.
hands
.
concat
(
cards
);
state
.
opHands
.
inner
=
state
.
opHands
.
inner
.
concat
(
cards
);
}
else
{
state
.
opHands
=
{
hands
:
cards
};
state
.
opHands
=
{
inner
:
cards
};
}
}
});
...
...
@@ -76,7 +73,7 @@ export const handsCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const
hands
=
judgeSelf
(
player
,
state
)
?
state
.
meHands
:
state
.
opHands
;
if
(
hands
)
{
for
(
let
hand
of
hands
.
hands
)
{
for
(
let
hand
of
hands
.
inner
)
{
for
(
let
meta
of
metas
)
{
if
(
hand
.
occupant
?.
id
===
meta
.
id
)
{
hand
.
occupant
=
meta
;
...
...
@@ -97,7 +94,7 @@ export const clearHandsIdleInteractivityImpl: CaseReducer<
const
hands
=
judgeSelf
(
player
,
state
)
?
state
.
meHands
:
state
.
opHands
;
if
(
hands
)
{
for
(
let
hand
of
hands
.
hands
)
{
for
(
let
hand
of
hands
.
inner
)
{
hand
.
idleInteractivities
=
[];
}
}
...
...
@@ -119,7 +116,7 @@ export const addHandsIdleInteractivityImpl: CaseReducer<
const
sequence
=
action
.
payload
.
sequence
;
const
interactivity
=
action
.
payload
.
interactivity
;
hands
.
hands
[
sequence
].
idleInteractivities
.
push
(
interactivity
);
hands
.
inner
[
sequence
].
idleInteractivities
.
push
(
interactivity
);
}
};
...
...
@@ -133,13 +130,13 @@ export const removeHandImpl: CaseReducer<
const
hands
=
judgeSelf
(
controler
,
state
)
?
state
.
meHands
:
state
.
opHands
;
if
(
hands
)
{
hands
.
hands
=
hands
.
hands
.
filter
(
hands
.
inner
=
hands
.
inner
.
filter
(
(
card
)
=>
card
.
location
.
sequence
!=
sequence
);
}
};
export
const
selectMeHands
=
(
state
:
RootState
)
=>
state
.
duel
.
meHands
||
{
hands
:
[]
};
state
.
duel
.
meHands
||
{
inner
:
[]
};
export
const
selectOpHands
=
(
state
:
RootState
)
=>
state
.
duel
.
opHands
||
{
hands
:
[]
};
state
.
duel
.
opHands
||
{
inner
:
[]
};
src/reducers/duel/magicSlice.ts
View file @
90419ce0
...
...
@@ -2,18 +2,20 @@ import { judgeSelf } from "./util";
import
{
PayloadAction
,
CaseReducer
,
createAsyncThunk
,
ActionReducerMapBuilder
,
}
from
"
@reduxjs/toolkit
"
;
import
{
DuelState
}
from
"
./mod
"
;
import
{
ygopro
}
from
"
../../api/ocgcore/idl/ocgcore
"
;
import
{
RootState
}
from
"
../../store
"
;
import
{
fetchCard
}
from
"
../../api/cards
"
;
import
{
CardState
,
InteractType
}
from
"
./generic
"
;
import
{
createAsyncMetaThunk
,
DuelFieldState
,
extendOccupant
,
extendPlaceInteractivity
,
clearPlaceInteractivities
,
}
from
"
./generic
"
;
export
interface
MagicState
{
magics
:
CardState
[];
}
export
interface
MagicState
extends
DuelFieldState
{}
// 初始化自己的魔法陷阱区状态
export
const
initMagicsImpl
:
CaseReducer
<
DuelState
,
PayloadAction
<
number
>>
=
(
...
...
@@ -22,7 +24,7 @@ export const initMagicsImpl: CaseReducer<DuelState, PayloadAction<number>> = (
)
=>
{
const
player
=
action
.
payload
;
const
magics
=
{
magics
:
[
inner
:
[
{
location
:
{
controler
:
player
,
...
...
@@ -81,20 +83,7 @@ export const addMagicPlaceInteractivitiesImpl: CaseReducer<
const
sequence
=
action
.
payload
[
1
];
const
magics
=
judgeSelf
(
controler
,
state
)
?
state
.
meMagics
:
state
.
opMagics
;
if
(
magics
)
{
for
(
const
magic
of
magics
.
magics
)
{
if
(
magic
.
location
.
sequence
==
sequence
)
{
magic
.
placeInteractivities
=
{
interactType
:
InteractType
.
PLACE_SELECTABLE
,
response
:
{
controler
,
zone
:
ygopro
.
CardZone
.
SZONE
,
sequence
,
},
};
}
}
}
extendPlaceInteractivity
(
magics
,
controler
,
sequence
,
ygopro
.
CardZone
.
SZONE
);
};
export
const
clearMagicPlaceInteractivitiesImpl
:
CaseReducer
<
...
...
@@ -104,35 +93,11 @@ export const clearMagicPlaceInteractivitiesImpl: CaseReducer<
const
player
=
action
.
payload
;
const
magics
=
judgeSelf
(
player
,
state
)
?
state
.
meMagics
:
state
.
opMagics
;
if
(
magics
)
{
for
(
const
magic
of
magics
.
magics
)
{
magic
.
placeInteractivities
=
undefined
;
}
}
clearPlaceInteractivities
(
magics
);
};
// 增加魔法陷阱
export
const
fetchMagicMeta
=
createAsyncThunk
(
"
duel/fetchMagicMeta
"
,
async
(
param
:
{
controler
:
number
;
sequence
:
number
;
position
:
ygopro
.
CardPosition
;
code
:
number
;
})
=>
{
const
code
=
param
.
code
;
const
meta
=
await
fetchCard
(
code
);
const
response
=
{
controler
:
param
.
controler
,
sequence
:
param
.
sequence
,
meta
,
};
return
response
;
}
);
export
const
fetchMagicMeta
=
createAsyncMetaThunk
(
"
duel/fetchMagicMeta
"
);
export
const
magicCase
=
(
builder
:
ActionReducerMapBuilder
<
DuelState
>
)
=>
{
builder
.
addCase
(
fetchMagicMeta
.
pending
,
(
state
,
action
)
=>
{
...
...
@@ -144,23 +109,9 @@ export const magicCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const
meta
=
{
id
:
code
,
data
:
{},
text
:
{}
};
if
(
judgeSelf
(
controler
,
state
))
{
if
(
state
.
meMagics
)
{
for
(
const
magic
of
state
.
meMagics
.
magics
)
{
if
(
magic
.
location
.
sequence
==
sequence
)
{
magic
.
occupant
=
meta
;
magic
.
location
.
position
=
position
;
}
}
}
extendOccupant
(
state
.
meMagics
,
meta
,
sequence
,
position
);
}
else
{
if
(
state
.
opMagics
)
{
for
(
const
magic
of
state
.
opMagics
.
magics
)
{
if
(
magic
.
location
.
sequence
==
sequence
)
{
magic
.
occupant
=
meta
;
magic
.
location
.
position
=
position
;
}
}
}
extendOccupant
(
state
.
opMagics
,
meta
,
sequence
,
position
);
}
});
builder
.
addCase
(
fetchMagicMeta
.
fulfilled
,
(
state
,
action
)
=>
{
...
...
@@ -169,26 +120,14 @@ export const magicCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const
meta
=
action
.
payload
.
meta
;
if
(
judgeSelf
(
controler
,
state
))
{
if
(
state
.
meMagics
)
{
for
(
const
magic
of
state
.
meMagics
.
magics
)
{
if
(
magic
.
location
.
sequence
==
sequence
)
{
magic
.
occupant
=
meta
;
}
}
}
extendOccupant
(
state
.
meMagics
,
meta
,
sequence
);
}
else
{
if
(
state
.
opMagics
)
{
for
(
const
magic
of
state
.
opMagics
.
magics
)
{
if
(
magic
.
location
.
sequence
==
sequence
)
{
magic
.
occupant
=
meta
;
}
}
}
extendOccupant
(
state
.
opMagics
,
meta
,
sequence
);
}
});
};
export
const
selectMeMagics
=
(
state
:
RootState
)
=>
state
.
duel
.
meMagics
||
{
magics
:
[]
};
state
.
duel
.
meMagics
||
{
inner
:
[]
};
export
const
selectOpMagics
=
(
state
:
RootState
)
=>
state
.
duel
.
opMagics
||
{
magics
:
[]
};
state
.
duel
.
opMagics
||
{
inner
:
[]
};
src/reducers/duel/mod.ts
View file @
90419ce0
...
...
@@ -56,6 +56,11 @@ import {
magicCase
,
}
from
"
./magicSlice
"
;
import
{
CemeteryState
,
initCemeteryImpl
,
cemeteryCase
}
from
"
./cemeretySlice
"
;
import
{
ExclusionState
,
initExclusionImpl
,
exclusionCase
,
}
from
"
./exclusionSlice
"
;
export
interface
DuelState
{
selfType
?:
number
;
...
...
@@ -74,6 +79,9 @@ export interface DuelState {
meCemetery
?:
CemeteryState
;
// 自己的墓地状态
opCemetery
?:
CemeteryState
;
// 对手的墓地状态
meExclusion
?:
ExclusionState
;
// 自己的除外区状态
opExclusion
?:
ExclusionState
;
// 对手的除外区状态
meTimeLimit
?:
TimeLimit
;
// 自己的计时
opTimeLimit
?:
TimeLimit
;
// 对手的计时
...
...
@@ -128,6 +136,9 @@ const duelSlice = createSlice({
// 墓地相关`Reducer`
initCemetery
:
initCemeteryImpl
,
// 除外区相关`Reducer`
initExclusion
:
initExclusionImpl
,
// UI相关`Reducer`
setCardModalIsOpen
:
setCardModalIsOpenImpl
,
setCardModalText
:
setCardModalTextImpl
,
...
...
@@ -154,6 +165,7 @@ const duelSlice = createSlice({
monsterCase
(
builder
);
magicCase
(
builder
);
cemeteryCase
(
builder
);
exclusionCase
(
builder
);
checkCardModalCase
(
builder
);
YesNoModalCase
(
builder
);
optionModalCase
(
builder
);
...
...
src/reducers/duel/monstersSlice.ts
View file @
90419ce0
...
...
@@ -2,18 +2,20 @@ import { judgeSelf } from "./util";
import
{
PayloadAction
,
CaseReducer
,
createAsyncThunk
,
ActionReducerMapBuilder
,
}
from
"
@reduxjs/toolkit
"
;
import
{
DuelState
}
from
"
./mod
"
;
import
{
ygopro
}
from
"
../../api/ocgcore/idl/ocgcore
"
;
import
{
RootState
}
from
"
../../store
"
;
import
{
fetchCard
}
from
"
../../api/cards
"
;
import
{
CardState
,
InteractType
}
from
"
./generic
"
;
import
{
DuelFieldState
,
createAsyncMetaThunk
,
extendOccupant
,
extendPlaceInteractivity
,
clearPlaceInteractivities
,
}
from
"
./generic
"
;
export
interface
MonsterState
{
monsters
:
CardState
[];
}
export
interface
MonsterState
extends
DuelFieldState
{}
// 初始化怪兽区状态
export
const
initMonstersImpl
:
CaseReducer
<
DuelState
,
PayloadAction
<
number
>>
=
(
...
...
@@ -22,7 +24,7 @@ export const initMonstersImpl: CaseReducer<DuelState, PayloadAction<number>> = (
)
=>
{
const
player
=
action
.
payload
;
const
monsters
=
{
monsters
:
[
inner
:
[
{
location
:
{
controler
:
player
,
...
...
@@ -83,20 +85,12 @@ export const addMonsterPlaceInteractivitiesImpl: CaseReducer<
const
monsters
=
judgeSelf
(
controler
,
state
)
?
state
.
meMonsters
:
state
.
opMonsters
;
if
(
monsters
)
{
for
(
const
monster
of
monsters
.
monsters
)
{
if
(
monster
.
location
.
sequence
==
sequence
)
{
monster
.
placeInteractivities
=
{
interactType
:
InteractType
.
PLACE_SELECTABLE
,
response
:
{
controler
,
zone
:
ygopro
.
CardZone
.
MZONE
,
sequence
,
},
};
}
}
}
extendPlaceInteractivity
(
monsters
,
controler
,
sequence
,
ygopro
.
CardZone
.
MZONE
);
};
export
const
clearMonsterPlaceInteractivitiesImpl
:
CaseReducer
<
...
...
@@ -109,34 +103,11 @@ export const clearMonsterPlaceInteractivitiesImpl: CaseReducer<
?
state
.
meMonsters
:
state
.
opMonsters
;
if
(
monsters
)
{
for
(
const
monster
of
monsters
.
monsters
)
{
monster
.
placeInteractivities
=
undefined
;
}
}
clearPlaceInteractivities
(
monsters
);
};
// 增加怪兽
export
const
fetchMonsterMeta
=
createAsyncThunk
(
"
duel/fetchMonsterMeta
"
,
async
(
param
:
{
controler
:
number
;
sequence
:
number
;
position
:
ygopro
.
CardPosition
;
code
:
number
;
})
=>
{
const
code
=
param
.
code
;
const
meta
=
await
fetchCard
(
code
);
const
response
=
{
controler
:
param
.
controler
,
sequence
:
param
.
sequence
,
meta
,
};
return
response
;
}
);
export
const
fetchMonsterMeta
=
createAsyncMetaThunk
(
"
duel/fetchMonsterMeta
"
);
export
const
monsterCase
=
(
builder
:
ActionReducerMapBuilder
<
DuelState
>
)
=>
{
builder
.
addCase
(
fetchMonsterMeta
.
pending
,
(
state
,
action
)
=>
{
...
...
@@ -148,23 +119,9 @@ export const monsterCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const
meta
=
{
id
:
code
,
data
:
{},
text
:
{}
};
if
(
judgeSelf
(
controler
,
state
))
{
if
(
state
.
meMonsters
)
{
for
(
const
monster
of
state
.
meMonsters
.
monsters
)
{
if
(
monster
.
location
.
sequence
==
sequence
)
{
monster
.
occupant
=
meta
;
monster
.
location
.
position
=
position
;
}
}
}
extendOccupant
(
state
.
meMonsters
,
meta
,
sequence
,
position
);
}
else
{
if
(
state
.
opMonsters
)
{
for
(
const
monster
of
state
.
opMonsters
.
monsters
)
{
if
(
monster
.
location
.
sequence
==
sequence
)
{
monster
.
occupant
=
meta
;
monster
.
location
.
position
=
position
;
}
}
}
extendOccupant
(
state
.
opMonsters
,
meta
,
sequence
,
position
);
}
});
builder
.
addCase
(
fetchMonsterMeta
.
fulfilled
,
(
state
,
action
)
=>
{
...
...
@@ -173,26 +130,14 @@ export const monsterCase = (builder: ActionReducerMapBuilder<DuelState>) => {
const
meta
=
action
.
payload
.
meta
;
if
(
judgeSelf
(
controler
,
state
))
{
if
(
state
.
meMonsters
)
{
for
(
const
monster
of
state
.
meMonsters
.
monsters
)
{
if
(
monster
.
location
.
sequence
==
sequence
)
{
monster
.
occupant
=
meta
;
}
}
}
extendOccupant
(
state
.
meMonsters
,
meta
,
sequence
);
}
else
{
if
(
state
.
opMonsters
)
{
for
(
const
monster
of
state
.
opMonsters
.
monsters
)
{
if
(
monster
.
location
.
sequence
==
sequence
)
{
monster
.
occupant
=
meta
;
}
}
}
extendOccupant
(
state
.
opMonsters
,
meta
,
sequence
);
}
});
};
export
const
selectMeMonsters
=
(
state
:
RootState
)
=>
state
.
duel
.
meMonsters
||
{
monsters
:
[]
};
state
.
duel
.
meMonsters
||
{
inner
:
[]
};
export
const
selectOpMonsters
=
(
state
:
RootState
)
=>
state
.
duel
.
opMonsters
||
{
monsters
:
[]
};
state
.
duel
.
opMonsters
||
{
inner
:
[]
};
src/ui/Duel/cemetery.tsx
View file @
90419ce0
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
*
as
CONFIG
from
"
../../config/ui
"
;
import
{
CardState
}
from
"
../../reducers/duel/generic
"
;
import
{
selectMeCemetery
,
selectOpCemetery
,
}
from
"
../../reducers/duel/cemeretySlice
"
;
import
{
store
}
from
"
../../store
"
;
import
{
useAppSelector
}
from
"
../../hook
"
;
import
{
useClick
}
from
"
./hook
"
;
import
{
useRef
}
from
"
react
"
;
import
{
setCardListModalInfo
,
setCardListModalIsOpen
,
}
from
"
../../reducers/duel/mod
"
;
import
SingleSlot
from
"
./singleSlot
"
;
const
shape
=
CONFIG
.
CemeterySlotShape
();
const
depth
=
0.02
;
const
Cemeteries
=
()
=>
{
const
meCemetery
=
useAppSelector
(
selectMeCemetery
).
cemetery
;
const
opCemetery
=
useAppSelector
(
selectOpCemetery
).
cemetery
;
const
meCemetery
=
useAppSelector
(
selectMeCemetery
).
inner
;
const
opCemetery
=
useAppSelector
(
selectOpCemetery
).
inner
;
return
(
<>
<
CCemetery
<
SingleSlot
state=
{
meCemetery
}
position=
{
cemeteryPosition
(
0
,
meCemetery
.
length
)
}
rotation=
{
CONFIG
.
CardSlotRotation
(
false
)
}
/>
<
CCemetery
<
SingleSlot
state=
{
opCemetery
}
position=
{
cemeteryPosition
(
1
,
opCemetery
.
length
)
}
rotation=
{
CONFIG
.
CardSlotRotation
(
true
)
}
...
...
@@ -37,60 +29,6 @@ const Cemeteries = () => {
);
};
const
CCemetery
=
(
props
:
{
state
:
CardState
[];
position
:
BABYLON
.
Vector3
;
rotation
:
BABYLON
.
Vector3
;
})
=>
{
const
boxRef
=
useRef
(
null
);
const
dispatch
=
store
.
dispatch
;
useClick
(
(
_event
)
=>
{
if
(
props
.
state
.
length
!=
0
)
{
dispatch
(
setCardListModalInfo
(
props
.
state
.
map
((
cemetery
)
=>
{
return
{
name
:
cemetery
.
occupant
?.
text
.
name
,
desc
:
cemetery
.
occupant
?.
text
.
desc
,
imgUrl
:
`https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/
${
cemetery
.
occupant
?.
id
}.
jpg
`,
};
})
)
);
dispatch(setCardListModalIsOpen(true));
}
},
boxRef,
[props.state]
);
return (
<box
name="cemetery"
ref={boxRef}
scaling={
new BABYLON.Vector3(
shape.width,
shape.height,
depth * props.state.length
)
}
position={props.position}
rotation={props.rotation}
>
<standardMaterial
name="cemetery-mat"
diffuseTexture={
new BABYLON.Texture(`
http
:
//localhost:3030/images/card_back.jpg`)
}
alpha={props.state.length == 0 ? 0 : 1}
/>
</box>
);
};
const
cemeteryPosition
=
(
player
:
number
,
cemeteryLength
:
number
)
=>
{
const
x
=
player
==
0
?
3.2
:
-
3.2
;
const
y
=
(
depth
*
cemeteryLength
)
/
2
+
CONFIG
.
Floating
;
...
...
src/ui/Duel/exclusion.tsx
View file @
90419ce0
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
*
as
CONFIG
from
"
../../config/ui
"
;
import
{
useAppSelector
}
from
"
../../hook
"
;
import
{
selectMeExclusion
,
selectopExclusion
,
}
from
"
../../reducers/duel/exclusionSlice
"
;
import
SingleSlot
from
"
./singleSlot
"
;
const
depth
=
0.02
;
const
Exclusion
=
()
=>
{
const
shape
=
CONFIG
.
ExclusionSlotShape
();
const
position
=
new
BABYLON
.
Vector3
(
3.2
,
CONFIG
.
Floating
,
-
0.7
);
const
rotation
=
CONFIG
.
ExclusionSlotRotation
();
const
meExclusion
=
useAppSelector
(
selectMeExclusion
).
inner
;
const
opExclusion
=
useAppSelector
(
selectopExclusion
).
inner
;
return
(
<
box
name=
"exclusion"
width=
{
shape
.
width
}
height=
{
shape
.
height
}
depth=
{
shape
.
depth
}
position=
{
position
}
rotation=
{
rotation
}
>
<
standardMaterial
name=
"exclusion-mat"
diffuseColor=
{
CONFIG
.
ExclusionColor
()
}
></
standardMaterial
>
</
box
>
<>
<
SingleSlot
state=
{
meExclusion
}
position=
{
exclusionPosition
(
0
,
meExclusion
.
length
)
}
rotation=
{
CONFIG
.
CardSlotRotation
(
false
)
}
/>
<
SingleSlot
state=
{
opExclusion
}
position=
{
exclusionPosition
(
1
,
opExclusion
.
length
)
}
rotation=
{
CONFIG
.
CardSlotRotation
(
true
)
}
/>
</>
);
};
const
exclusionPosition
=
(
player
:
number
,
exclusionLength
:
number
)
=>
{
const
x
=
player
==
0
?
3.2
:
-
3.2
;
const
y
=
(
depth
*
exclusionLength
)
/
2
+
CONFIG
.
Floating
;
const
z
=
player
==
0
?
-
0.7
:
0.7
;
return
new
BABYLON
.
Vector3
(
x
,
y
,
z
);
};
export
default
Exclusion
;
src/ui/Duel/hands.tsx
View file @
90419ce0
...
...
@@ -22,9 +22,9 @@ const handShape = CONFIG.HandShape();
const
handRotation
=
CONFIG
.
HandRotation
();
const
Hands
=
()
=>
{
const
meHands
=
useAppSelector
(
selectMeHands
).
hands
;
const
meHands
=
useAppSelector
(
selectMeHands
).
inner
;
const
meHandPositions
=
handPositons
(
0
,
meHands
);
const
opHands
=
useAppSelector
(
selectOpHands
).
hands
;
const
opHands
=
useAppSelector
(
selectOpHands
).
inner
;
const
opHandPositions
=
handPositons
(
1
,
opHands
);
return
(
...
...
src/ui/Duel/magics.tsx
View file @
90419ce0
...
...
@@ -22,9 +22,9 @@ const gap = 1.05;
const
shape
=
CONFIG
.
CardSlotShape
();
const
Magics
=
()
=>
{
const
meMagics
=
useAppSelector
(
selectMeMagics
).
magics
;
const
meMagics
=
useAppSelector
(
selectMeMagics
).
inner
;
const
meMagicPositions
=
magicPositions
(
0
,
meMagics
);
const
opMagics
=
useAppSelector
(
selectOpMagics
).
magics
;
const
opMagics
=
useAppSelector
(
selectOpMagics
).
inner
;
const
opMagicPositions
=
magicPositions
(
1
,
opMagics
);
return
(
...
...
src/ui/Duel/monsters.tsx
View file @
90419ce0
...
...
@@ -26,9 +26,9 @@ const left = -2.15; // TODO: config
const
gap
=
1.05
;
const
Monsters
=
()
=>
{
const
meMonsters
=
useAppSelector
(
selectMeMonsters
).
monsters
;
const
meMonsters
=
useAppSelector
(
selectMeMonsters
).
inner
;
const
meMonsterPositions
=
monsterPositions
(
0
,
meMonsters
);
const
opMonsters
=
useAppSelector
(
selectOpMonsters
).
monsters
;
const
opMonsters
=
useAppSelector
(
selectOpMonsters
).
inner
;
const
opMonsterPositions
=
monsterPositions
(
1
,
opMonsters
);
return
(
...
...
src/ui/Duel/singleSlot.tsx
0 → 100644
View file @
90419ce0
import
*
as
BABYLON
from
"
@babylonjs/core
"
;
import
*
as
CONFIG
from
"
../../config/ui
"
;
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
"
;
const
shape
=
CONFIG
.
SingleSlotShape
;
const
depth
=
0.02
;
const
SingleSlot
=
(
props
:
{
state
:
CardState
[];
position
:
BABYLON
.
Vector3
;
rotation
:
BABYLON
.
Vector3
;
})
=>
{
const
boxRef
=
useRef
(
null
);
const
dispatch
=
store
.
dispatch
;
useClick
(
(
_event
)
=>
{
if
(
props
.
state
.
length
!=
0
)
{
dispatch
(
setCardListModalInfo
(
props
.
state
.
map
((
item
)
=>
{
return
{
name
:
item
.
occupant
?.
text
.
name
,
desc
:
item
.
occupant
?.
text
.
desc
,
imgUrl
:
`https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/
${
item
.
occupant
?.
id
}.
jpg
`,
};
})
)
);
dispatch(setCardListModalIsOpen(true));
}
},
boxRef,
[props.state]
);
return (
<box
name="single-slot"
ref={boxRef}
scaling={
new BABYLON.Vector3(
shape.width,
shape.height,
depth * props.state.length
)
}
position={props.position}
rotation={props.rotation}
>
<standardMaterial
name="single-slot-mat"
diffuseTexture={
new BABYLON.Texture(`
http
:
//localhost:3030/images/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