Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
N
Neos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
1
Issues
1
List
Boards
Labels
Service Desk
Milestones
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Neos
Commits
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
!71
parents
9c3989e3
b33802fc
Pipeline
#19449
passed with stages
in 4 minutes and 31 seconds
Changes
14
Pipelines
1
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