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 {
exclusionCase,
} from "./exclusionSlice";
import { DeckState, initDeckImpl } from "./deckSlice";
import { FieldState, initFieldImpl } from "./fieldSlice";
export interface DuelState {
selfType?: number;
......@@ -86,6 +87,9 @@ export interface DuelState {
meDeck?: DeckState; // 自己的卡组状态
opDeck?: DeckState; // 对手的卡组状态
meField?: FieldState; // 自己的场地区状态
opField?: FieldState; // 对手的场地区状态
meTimeLimit?: TimeLimit; // 自己的计时
opTimeLimit?: TimeLimit; // 对手的计时
......@@ -146,6 +150,9 @@ const duelSlice = createSlice({
// 卡组相关`Reducer`
initDeck: initDeckImpl,
// 场地区相关`Reducer`
initField: initFieldImpl,
// UI相关`Reducer`
setCardModalIsOpen: setCardModalIsOpenImpl,
setCardModalText: setCardModalTextImpl,
......@@ -214,6 +221,8 @@ export const {
setOptionModalIsOpen,
resetOptionModal,
initDeck,
initExclusion,
initField,
} = duelSlice.actions;
export const selectDuelHsStart = (state: RootState) => {
return state.duel.meInitInfo != null;
......
......@@ -7,6 +7,8 @@ import {
initMagics,
initCemetery,
initDeck,
initExclusion,
initField,
} from "../../reducers/duel/mod";
export default (
......@@ -42,4 +44,8 @@ export default (
dispatch(initCemetery(1));
dispatch(initDeck({ player: 0, deskSize: start.deckSize1 }));
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 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 shape = CONFIG.FieldSlotShape();
const position = new BABYLON.Vector3(
-3.3,
shape.depth / 2 + CONFIG.Floating,
-2.0
);
const rotation = CONFIG.FieldSlotRotation();
const meField = useAppSelector(selectMeField)?.inner;
const opField = useAppSelector(selectOpField)?.inner;
return (
<box
name="field"
width={shape.width}
height={shape.height}
depth={shape.depth}
position={position}
rotation={rotation}
>
<standardMaterial name="field-mat" diffuseColor={CONFIG.FieldColor()} />
</box>
<>
{meField ? (
<FixedSlot
state={meField}
position={fieldPosition(0)}
rotation={CONFIG.CardSlotRotation(false)}
/>
) : (
<></>
)}
{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;
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 CONFIG from "../../config/ui";
import { selectMeMagics, selectOpMagics } from "../../reducers/duel/magicSlice";
import { useClick } from "./hook";
import { CardState } from "../../reducers/duel/generic";
import { store } from "../../store";
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 FixedSlot from "./fixedSlot";
// TODO: use config
const left = -2.15;
......@@ -31,7 +21,7 @@ const Magics = () => {
<>
{zip(meMagics, meMagicPositions).map(([magic, position]) => {
return (
<CMagic
<FixedSlot
state={magic}
key={magic.location.sequence}
position={position}
......@@ -41,7 +31,7 @@ const Magics = () => {
})}
{zip(opMagics, opMagicPositions).map(([magic, position]) => {
return (
<CMagic
<FixedSlot
state={magic}
key={magic.location.sequence}
position={position}
......@@ -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 x = (sequence: number) =>
player == 0 ? left + gap * sequence : -left - gap * sequence;
......
......@@ -35,6 +35,7 @@ const NeosDuel = () => (
<Deck />
<Cemeteries />
<Exclusion />
<Field />
<Ground />
</Provider>
</Scene>
......
import * as BABYLON from "@babylonjs/core";
import * as CONFIG from "../../config/ui";
import { useClick } from "./hook";
import { store } from "../../store";
import { CardState } from "../../reducers/duel/generic";
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 {
selectMeMonsters,
selectOpMonsters,
} from "../../reducers/duel/monstersSlice";
import { ygopro } from "../../api/ocgcore/idl/ocgcore";
import { zip } from "./util";
import FixedSlot from "./fixedSlot";
const shape = CONFIG.CardSlotShape();
const left = -2.15; // TODO: config
......@@ -35,7 +24,7 @@ const Monsters = () => {
<>
{zip(meMonsters, meMonsterPositions).map(([monster, position], idx) => {
return (
<CommonMonster
<FixedSlot
state={monster}
key={idx}
position={position}
......@@ -46,7 +35,7 @@ const Monsters = () => {
})}
{zip(opMonsters, opMonsterPositions).map(([monster, position], idx) => {
return (
<CommonMonster
<FixedSlot
state={monster}
key={idx}
position={position}
......@@ -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
const ExtraMonsters = () => {
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