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