Commit 26ff2f20 authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/field' into 'main'

Feat/field

See merge request !73
parents 6b2d3fe5 e5e57d18
Pipeline #19472 passed with stages
in 3 minutes and 52 seconds
import { judgeSelf } from "./util";
import { DuelState } from "./mod";
import { RootState } from "../../store";
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { PayloadAction, CaseReducer } from "@reduxjs/toolkit";
import { CardState } from "./generic";
export interface FieldState {
inner?: CardState;
}
// 初始化场地区状态
export const initFieldImpl: CaseReducer<DuelState, PayloadAction<number>> = (
state,
action
) => {
const player = action.payload;
if (judgeSelf(player, state)) {
state.meField = {
inner: {
location: {
controler: player,
sequence: 0,
location: ygopro.CardZone.ONFIELD,
},
idleInteractivities: [],
},
};
} else {
state.opField = {
inner: {
location: {
controler: player,
sequence: 0,
location: ygopro.CardZone.ONFIELD,
},
idleInteractivities: [],
},
};
}
};
export const selectMeField = (state: RootState) => state.duel.meField;
export const selectOpField = (state: RootState) => state.duel.opField;
...@@ -62,6 +62,7 @@ import { ...@@ -62,6 +62,7 @@ import {
exclusionCase, exclusionCase,
} from "./exclusionSlice"; } from "./exclusionSlice";
import { DeckState, initDeckImpl } from "./deckSlice"; import { DeckState, initDeckImpl } from "./deckSlice";
import { FieldState, initFieldImpl } from "./fieldSlice";
export interface DuelState { export interface DuelState {
selfType?: number; selfType?: number;
...@@ -86,6 +87,9 @@ export interface DuelState { ...@@ -86,6 +87,9 @@ export interface DuelState {
meDeck?: DeckState; // 自己的卡组状态 meDeck?: DeckState; // 自己的卡组状态
opDeck?: DeckState; // 对手的卡组状态 opDeck?: DeckState; // 对手的卡组状态
meField?: FieldState; // 自己的场地区状态
opField?: FieldState; // 对手的场地区状态
meTimeLimit?: TimeLimit; // 自己的计时 meTimeLimit?: TimeLimit; // 自己的计时
opTimeLimit?: TimeLimit; // 对手的计时 opTimeLimit?: TimeLimit; // 对手的计时
...@@ -146,6 +150,9 @@ const duelSlice = createSlice({ ...@@ -146,6 +150,9 @@ const duelSlice = createSlice({
// 卡组相关`Reducer` // 卡组相关`Reducer`
initDeck: initDeckImpl, initDeck: initDeckImpl,
// 场地区相关`Reducer`
initField: initFieldImpl,
// UI相关`Reducer` // UI相关`Reducer`
setCardModalIsOpen: setCardModalIsOpenImpl, setCardModalIsOpen: setCardModalIsOpenImpl,
setCardModalText: setCardModalTextImpl, setCardModalText: setCardModalTextImpl,
...@@ -214,6 +221,8 @@ export const { ...@@ -214,6 +221,8 @@ export const {
setOptionModalIsOpen, setOptionModalIsOpen,
resetOptionModal, resetOptionModal,
initDeck, initDeck,
initExclusion,
initField,
} = duelSlice.actions; } = duelSlice.actions;
export const selectDuelHsStart = (state: RootState) => { export const selectDuelHsStart = (state: RootState) => {
return state.duel.meInitInfo != null; return state.duel.meInitInfo != null;
......
...@@ -7,6 +7,8 @@ import { ...@@ -7,6 +7,8 @@ import {
initMagics, initMagics,
initCemetery, initCemetery,
initDeck, initDeck,
initExclusion,
initField,
} from "../../reducers/duel/mod"; } from "../../reducers/duel/mod";
export default ( export default (
...@@ -42,4 +44,8 @@ export default ( ...@@ -42,4 +44,8 @@ export default (
dispatch(initCemetery(1)); dispatch(initCemetery(1));
dispatch(initDeck({ player: 0, deskSize: start.deckSize1 })); dispatch(initDeck({ player: 0, deskSize: start.deckSize1 }));
dispatch(initDeck({ player: 1, deskSize: start.deckSize2 })); dispatch(initDeck({ player: 1, deskSize: start.deckSize2 }));
dispatch(initExclusion(0));
dispatch(initExclusion(1));
dispatch(initField(0));
dispatch(initField(1));
}; };
import * as BABYLON from "@babylonjs/core"; import * as BABYLON from "@babylonjs/core";
import * as CONFIG from "../../config/ui"; import * as CONFIG from "../../config/ui";
import { useAppSelector } from "../../hook";
import { selectMeField, selectOpField } from "../../reducers/duel/fieldSlice";
import FixedSlot from "./fixedSlot";
import { Depth } from "./singleSlot";
const Field = () => { const Field = () => {
const shape = CONFIG.FieldSlotShape(); const meField = useAppSelector(selectMeField)?.inner;
const position = new BABYLON.Vector3( const opField = useAppSelector(selectOpField)?.inner;
-3.3,
shape.depth / 2 + CONFIG.Floating,
-2.0
);
const rotation = CONFIG.FieldSlotRotation();
return ( return (
<box <>
name="field" {meField ? (
width={shape.width} <FixedSlot
height={shape.height} state={meField}
depth={shape.depth} position={fieldPosition(0)}
position={position} rotation={CONFIG.CardSlotRotation(false)}
rotation={rotation} />
> ) : (
<standardMaterial name="field-mat" diffuseColor={CONFIG.FieldColor()} /> <></>
</box> )}
{opField ? (
<FixedSlot
state={opField}
position={fieldPosition(1)}
rotation={CONFIG.CardSlotRotation(true)}
/>
) : (
<></>
)}
</>
); );
}; };
const fieldPosition = (player: number) => {
const x = player == 0 ? -3.3 : 3.3;
const y = Depth / 2 + CONFIG.Floating;
const z = player == 0 ? -2.0 : 2.0;
return new BABYLON.Vector3(x, y, z);
};
export default Field; export default Field;
import * as BABYLON from "@babylonjs/core";
import * as CONFIG from "../../config/ui";
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 {
clearMonsterPlaceInteractivities,
setCardModalImgUrl,
setCardModalInteractivies,
setCardModalIsOpen,
setCardModalText,
} from "../../reducers/duel/mod";
const shape = CONFIG.CardSlotShape();
const FixedSlot = (props: {
state: CardState;
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
deffenseRotation?: BABYLON.Vector3;
}) => {
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 || CONFIG.CardSlotDefenceRotation()
: 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(clearMonsterPlaceInteractivities(0));
dispatch(clearMonsterPlaceInteractivities(1));
} else if (props.state.occupant) {
dispatch(
setCardModalText([
props.state.occupant.text.name,
props.state.occupant.text.desc,
])
);
dispatch(
setCardModalImgUrl(
`https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/${props.state.occupant.id}.jpg`
)
);
dispatch(setCardModalInteractivies([])); // TODO
dispatch(setCardModalIsOpen(true));
}
},
planeRef,
[props.state]
);
return (
<plane
name={`fixedslot-${props.state.location.sequence}`}
ref={planeRef}
width={shape.width}
height={shape.height}
position={props.position}
rotation={rotation}
enableEdgesRendering
edgesWidth={
props.state.placeInteractivities ||
props.state.idleInteractivities.length > 0
? edgesWidth
: 0
}
edgesColor={edgesColor}
>
<standardMaterial
name={`fixedslot-mat-${props.state.location.sequence}`}
diffuseTexture={
props.state.occupant
? faceDown
? new BABYLON.Texture(
`http://localhost:3030/images/card_back.jpg`
)
: new BABYLON.Texture(
`https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/${props.state.occupant.id}.jpg`
)
: new BABYLON.Texture(`http://localhost:3030/images/card_slot.png`)
}
alpha={props.state.occupant ? 1 : 0}
></standardMaterial>
</plane>
);
};
export default FixedSlot;
import * as BABYLON from "@babylonjs/core"; import * as BABYLON from "@babylonjs/core";
import * as CONFIG from "../../config/ui"; import * as CONFIG from "../../config/ui";
import { selectMeMagics, selectOpMagics } from "../../reducers/duel/magicSlice"; import { selectMeMagics, selectOpMagics } from "../../reducers/duel/magicSlice";
import { useClick } from "./hook";
import { CardState } from "../../reducers/duel/generic"; import { CardState } from "../../reducers/duel/generic";
import { store } from "../../store";
import { useAppSelector } from "../../hook"; import { useAppSelector } from "../../hook";
import { useRef } from "react";
import { sendSelectPlaceResponse } from "../../api/ocgcore/ocgHelper";
import {
clearMagicPlaceInteractivities,
setCardModalImgUrl,
setCardModalIsOpen,
setCardModalText,
} from "../../reducers/duel/mod";
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { zip } from "./util"; import { zip } from "./util";
import FixedSlot from "./fixedSlot";
// TODO: use config // TODO: use config
const left = -2.15; const left = -2.15;
...@@ -31,7 +21,7 @@ const Magics = () => { ...@@ -31,7 +21,7 @@ const Magics = () => {
<> <>
{zip(meMagics, meMagicPositions).map(([magic, position]) => { {zip(meMagics, meMagicPositions).map(([magic, position]) => {
return ( return (
<CMagic <FixedSlot
state={magic} state={magic}
key={magic.location.sequence} key={magic.location.sequence}
position={position} position={position}
...@@ -41,7 +31,7 @@ const Magics = () => { ...@@ -41,7 +31,7 @@ const Magics = () => {
})} })}
{zip(opMagics, opMagicPositions).map(([magic, position]) => { {zip(opMagics, opMagicPositions).map(([magic, position]) => {
return ( return (
<CMagic <FixedSlot
state={magic} state={magic}
key={magic.location.sequence} key={magic.location.sequence}
position={position} position={position}
...@@ -53,78 +43,6 @@ const Magics = () => { ...@@ -53,78 +43,6 @@ const Magics = () => {
); );
}; };
const CMagic = (props: {
state: CardState;
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
}) => {
const state = props.state;
const planeRef = useRef(null);
const faceDown =
state.location.position === ygopro.CardPosition.FACEDOWN ||
state.location.position === ygopro.CardPosition.FACEDOWN_ATTACK ||
state.location.position === ygopro.CardPosition.FACEDOWN_DEFENSE;
const edgesWidth = 2.0;
const edgesColor = BABYLON.Color4.FromColor3(BABYLON.Color3.Yellow());
const dispatch = store.dispatch;
useClick(
(_event) => {
if (state.placeInteractivities) {
sendSelectPlaceResponse(state.placeInteractivities.response);
dispatch(clearMagicPlaceInteractivities(0));
dispatch(clearMagicPlaceInteractivities(1));
} else if (state.occupant) {
dispatch(
setCardModalText([state.occupant.text.name, state.occupant.text.desc])
);
dispatch(
setCardModalImgUrl(
`https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/${state.occupant.id}.jpg`
)
);
dispatch(setCardModalIsOpen(true));
}
},
planeRef,
[state]
);
return (
<plane
name={`magic-${state.location.sequence}`}
ref={planeRef}
width={shape.width}
height={shape.height}
position={props.position}
rotation={props.rotation}
enableEdgesRendering
edgesWidth={
state.placeInteractivities || state.idleInteractivities.length > 0
? edgesWidth
: 0
}
edgesColor={edgesColor}
>
<standardMaterial
name={`magic-mat-${props.state.location.sequence}`}
diffuseTexture={
state.occupant
? faceDown
? new BABYLON.Texture(
`http://localhost:3030/images/card_back.jpg`
)
: new BABYLON.Texture(
`https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/${state.occupant.id}.jpg`
)
: new BABYLON.Texture(`http://localhost:3030/images/card_slot.png`)
}
alpha={state.occupant ? 1 : 0}
></standardMaterial>
</plane>
);
};
const magicPositions = (player: number, magics: CardState[]) => { const magicPositions = (player: number, magics: CardState[]) => {
const x = (sequence: number) => const x = (sequence: number) =>
player == 0 ? left + gap * sequence : -left - gap * sequence; player == 0 ? left + gap * sequence : -left - gap * sequence;
......
...@@ -35,6 +35,7 @@ const NeosDuel = () => ( ...@@ -35,6 +35,7 @@ const NeosDuel = () => (
<Deck /> <Deck />
<Cemeteries /> <Cemeteries />
<Exclusion /> <Exclusion />
<Field />
<Ground /> <Ground />
</Provider> </Provider>
</Scene> </Scene>
......
import * as BABYLON from "@babylonjs/core"; import * as BABYLON from "@babylonjs/core";
import * as CONFIG from "../../config/ui"; import * as CONFIG from "../../config/ui";
import { useClick } from "./hook";
import { store } from "../../store";
import { CardState } from "../../reducers/duel/generic"; import { CardState } from "../../reducers/duel/generic";
import "react-babylonjs"; import "react-babylonjs";
import { useRef } from "react";
import { sendSelectPlaceResponse } from "../../api/ocgcore/ocgHelper";
import {
clearMonsterPlaceInteractivities,
setCardModalImgUrl,
setCardModalInteractivies,
setCardModalIsOpen,
setCardModalText,
} from "../../reducers/duel/mod";
import { useAppSelector } from "../../hook"; import { useAppSelector } from "../../hook";
import { import {
selectMeMonsters, selectMeMonsters,
selectOpMonsters, selectOpMonsters,
} from "../../reducers/duel/monstersSlice"; } from "../../reducers/duel/monstersSlice";
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { zip } from "./util"; import { zip } from "./util";
import FixedSlot from "./fixedSlot";
const shape = CONFIG.CardSlotShape(); const shape = CONFIG.CardSlotShape();
const left = -2.15; // TODO: config const left = -2.15; // TODO: config
...@@ -35,7 +24,7 @@ const Monsters = () => { ...@@ -35,7 +24,7 @@ const Monsters = () => {
<> <>
{zip(meMonsters, meMonsterPositions).map(([monster, position], idx) => { {zip(meMonsters, meMonsterPositions).map(([monster, position], idx) => {
return ( return (
<CommonMonster <FixedSlot
state={monster} state={monster}
key={idx} key={idx}
position={position} position={position}
...@@ -46,7 +35,7 @@ const Monsters = () => { ...@@ -46,7 +35,7 @@ const Monsters = () => {
})} })}
{zip(opMonsters, opMonsterPositions).map(([monster, position], idx) => { {zip(opMonsters, opMonsterPositions).map(([monster, position], idx) => {
return ( return (
<CommonMonster <FixedSlot
state={monster} state={monster}
key={idx} key={idx}
position={position} position={position}
...@@ -61,91 +50,6 @@ const Monsters = () => { ...@@ -61,91 +50,6 @@ const Monsters = () => {
); );
}; };
const CommonMonster = (props: {
state: CardState;
position: BABYLON.Vector3;
rotation: BABYLON.Vector3;
deffenseRotation: BABYLON.Vector3;
}) => {
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
: 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(clearMonsterPlaceInteractivities(0));
dispatch(clearMonsterPlaceInteractivities(1));
} else if (props.state.occupant) {
dispatch(
setCardModalText([
props.state.occupant.text.name,
props.state.occupant.text.desc,
])
);
dispatch(
setCardModalImgUrl(
`https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/${props.state.occupant.id}.jpg`
)
);
dispatch(setCardModalInteractivies([])); // TODO
dispatch(setCardModalIsOpen(true));
}
},
planeRef,
[props.state]
);
return (
<plane
name={`monster-${props.state.location.sequence}`}
ref={planeRef}
width={shape.width}
height={shape.height}
position={props.position}
rotation={rotation}
enableEdgesRendering
edgesWidth={
props.state.placeInteractivities ||
props.state.idleInteractivities.length > 0
? edgesWidth
: 0
}
edgesColor={edgesColor}
>
<standardMaterial
name={`monster-mat-${props.state.location.sequence}`}
diffuseTexture={
props.state.occupant
? faceDown
? new BABYLON.Texture(
`http://localhost:3030/images/card_back.jpg`
)
: new BABYLON.Texture(
`https://cdn02.moecube.com:444/images/ygopro-images-zh-CN/${props.state.occupant.id}.jpg`
)
: new BABYLON.Texture(`http://localhost:3030/images/card_slot.png`)
}
alpha={props.state.occupant ? 1 : 0}
></standardMaterial>
</plane>
);
};
// TODO: use props and redux // TODO: use props and redux
const ExtraMonsters = () => { const ExtraMonsters = () => {
const xs = [-1.1, 1]; const xs = [-1.1, 1];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment