Commit e92c2d4b authored by timel's avatar timel

feat: token animation

parent 875e48fc
Pipeline #22593 passed with stages
in 11 minutes and 2 seconds
......@@ -157,7 +157,7 @@ export default async (move: MsgMove) => {
target.location = to;
// 维护完了之后,开始动画
await eventbus.call(Task.Move, target.uuid);
await eventbus.call(Task.Move, target.uuid, from.zone);
// 如果from或者to是手卡,那么需要刷新除了这张卡之外,这个玩家的所有手卡
if ([from.zone, to.zone].includes(HAND)) {
await Promise.all(
......
......@@ -31,6 +31,7 @@ import {
moveToGround,
moveToHand,
moveToOutside,
moveToToken,
} from "./springs";
import type { SpringApiProps } from "./springs/types";
......@@ -59,26 +60,27 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
} satisfies SpringApiProps)
);
const move = async (zone: ygopro.CardZone) => {
switch (zone) {
// FIXME: move不应该只根据目的地判断,还要根据先前的位置判断。例子是Token。
const move = async (toZone: ygopro.CardZone, fromZone?: ygopro.CardZone) => {
switch (toZone) {
case MZONE:
case SZONE:
await moveToGround({ card: state, api });
await moveToGround({ card: state, api, fromZone });
break;
case HAND:
await moveToHand({ card: state, api });
await moveToHand({ card: state, api, fromZone });
break;
case DECK:
case EXTRA:
await moveToDeck({ card: state, api });
await moveToDeck({ card: state, api, fromZone });
break;
case GRAVE:
case REMOVED:
await moveToOutside({ card: state, api });
await moveToOutside({ card: state, api, fromZone });
break;
case TZONE:
// FIXME: 这里应该实现一个衍生物消散的动画,现在暂时让它在动画在展示上回到卡组
await moveToDeck({ card: state, api });
await moveToToken({ card: state, api, fromZone });
break;
}
};
......@@ -102,11 +104,14 @@ export const Card: React.FC<{ idx: number }> = React.memo(({ idx }) => {
});
useEffect(() => {
eventbus.register(Task.Move, async (uuid: string) => {
if (uuid === state.uuid) {
await addToAnimation(() => move(state.location.zone));
eventbus.register(
Task.Move,
async (uuid: string, fromZone?: ygopro.CardZone) => {
if (uuid === state.uuid) {
await addToAnimation(() => move(state.location.zone, fromZone));
}
}
});
);
eventbus.register(Task.Focus, async (uuid: string) => {
if (uuid === state.uuid) {
......
......@@ -5,7 +5,7 @@ import { ygopro } from "@/api";
import { CardType, isMe } from "@/stores";
import { matConfig } from "../../utils";
import { SpringApi } from "./types";
import type { SpringApi } from "./types";
import { asyncStart } from "./utils";
const { BLOCK_WIDTH, BLOCK_HEIGHT_M, BLOCK_HEIGHT_S, COL_GAP, ROW_GAP } =
......
import { ygopro } from "@/api";
import { type CardType, matStore } from "@/stores";
import { SpringApi } from "./types";
import type { SpringApi } from "./types";
import { asyncStart } from "./utils";
/** 发动效果的动画 */
......
......@@ -4,3 +4,4 @@ export * from "./moveToDeck";
export * from "./moveToGround";
export * from "./moveToHand";
export * from "./moveToOutside";
export * from "./moveToToken";
import { ygopro } from "@/api";
import { type CardType, isMe } from "@/stores";
import { isMe } from "@/stores";
import { matConfig } from "../../utils";
import { SpringApi } from "./types";
import { asyncStart } from "./utils";
import { asyncStart, type MoveFunc } from "./utils";
const {
BLOCK_WIDTH,
......@@ -19,7 +18,7 @@ const {
const { DECK, EXTRA } = ygopro.CardZone;
export const moveToDeck = async (props: { card: CardType; api: SpringApi }) => {
export const moveToDeck: MoveFunc = async (props) => {
const { card, api } = props;
// report
const { location } = card;
......
import { easings } from "@react-spring/web";
import { ygopro } from "@/api";
import { type CardType, isMe } from "@/stores";
import { isMe } from "@/stores";
import { matConfig } from "../../utils";
import { SpringApi } from "./types";
import { asyncStart } from "./utils";
import { asyncStart, type MoveFunc } from "./utils";
const {
BLOCK_WIDTH,
......@@ -16,13 +15,10 @@ const {
ROW_GAP,
} = matConfig;
const { MZONE, SZONE } = ygopro.CardZone;
const { MZONE, SZONE, TZONE } = ygopro.CardZone;
export const moveToGround = async (props: {
card: CardType;
api: SpringApi;
}) => {
const { card, api } = props;
export const moveToGround: MoveFunc = async (props) => {
const { card, api, fromZone } = props;
const { location } = card;
......@@ -84,26 +80,41 @@ export const moveToGround = async (props: {
let rz = isMe(controller) ? 0 : 180;
rz += defence ? 90 : 0;
const ry = [
ygopro.CardPosition.FACEDOWN,
ygopro.CardPosition.FACEDOWN_ATTACK,
ygopro.CardPosition.FACEDOWN_DEFENSE,
].includes(position ?? 5)
? 180
: 0;
// 动画
if (fromZone === TZONE) {
// 如果是Token,直接先移动到那个位置,然后再放大
api.set({
x,
y,
ry,
rz,
height: 0,
});
} else {
await asyncStart(api)({
x,
y,
height,
z: is_overlay ? 120 : 200,
ry,
rz,
config: {
// mass: 0.5,
easing: easings.easeInOutSine,
},
});
}
await asyncStart(api)({
x,
y,
height,
z: is_overlay ? 120 : 200,
ry: [
ygopro.CardPosition.FACEDOWN,
ygopro.CardPosition.FACEDOWN_ATTACK,
ygopro.CardPosition.FACEDOWN_DEFENSE,
].includes(position ?? 5)
? 180
: 0,
rz,
config: {
// mass: 0.5,
easing: easings.easeInOutSine,
},
});
await asyncStart(api)({
z: 0,
zIndex: is_overlay ? 1 : 3,
config: {
......
import { ygopro } from "@/api";
import { cardStore, type CardType, isMe } from "@/stores";
import { cardStore, isMe } from "@/stores";
import { matConfig } from "../../utils";
import { SpringApi } from "./types";
import { asyncStart } from "./utils";
import { asyncStart, type MoveFunc } from "./utils";
const {
BLOCK_HEIGHT_M,
......@@ -17,7 +16,7 @@ const {
const { HAND } = ygopro.CardZone;
export const moveToHand = async (props: { card: CardType; api: SpringApi }) => {
export const moveToHand: MoveFunc = async (props) => {
const { card, api } = props;
const { sequence, controller } = card.location;
// 手卡会有很复杂的计算...
......
import { ygopro } from "@/api";
import { type CardType, isMe } from "@/stores";
import { isMe } from "@/stores";
import { matConfig } from "../../utils";
import { SpringApi } from "./types";
import { asyncStart } from "./utils";
import { asyncStart, type MoveFunc } from "./utils";
const { BLOCK_WIDTH, BLOCK_HEIGHT_M, BLOCK_HEIGHT_S, COL_GAP, ROW_GAP } =
matConfig;
const { GRAVE } = ygopro.CardZone;
export const moveToOutside = async (props: {
card: CardType;
api: SpringApi;
}) => {
export const moveToOutside: MoveFunc = async (props) => {
const { card, api } = props;
// report
const { zone, controller, position, sequence } = card.location;
......
import { asyncStart, type MoveFunc } from "./utils";
export const moveToToken: MoveFunc = async (props) => {
const { api } = props;
await asyncStart(api)({
height: 0,
});
};
import { type SpringConfig, type SpringRef } from "@react-spring/web";
import type { ygopro } from "@/api";
import { type CardType } from "@/stores";
import type { SpringApi } from "./types";
export const asyncStart = <T extends {}>(api: SpringRef<T>) => {
return (p: Partial<T> & { config?: SpringConfig }) =>
new Promise((resolve) => {
......@@ -9,3 +14,9 @@ export const asyncStart = <T extends {}>(api: SpringRef<T>) => {
});
});
};
export type MoveFunc = (props: {
card: CardType;
api: SpringApi;
fromZone?: ygopro.CardZone;
}) => Promise<void>;
......@@ -28,7 +28,6 @@ import {
} from "@/api";
import { cardStore, matStore } from "@/stores";
import PhaseType = ygopro.StocGameMessage.MsgNewPhase.PhaseType;
import { Timer } from "../Timer";
const { phase } = matStore;
const { useToken } = theme;
......
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