Commit 3184d713 authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/animation/chain' into 'main'

Feat/animation/chain

See merge request mycard/Neos!190
parents 65312b5b 65ec7fe5
Pipeline #21643 passed with stages
in 11 minutes and 56 seconds
neos-protobuf @ c83175eb
Subproject commit 30d0ceb9d06259c09f3dfd36c3872bd1e9c02b88 Subproject commit c83175eb31577752e3ea02100e5f782f4417b654
...@@ -71,7 +71,10 @@ ...@@ -71,7 +71,10 @@
"hint":{ "hint":{
"waitingDuration":1.5, "waitingDuration":1.5,
"maxCount": 1 "maxCount": 1
} },
"commonDelay": 200,
"moveDelay": 500,
"chainingDelay": 800
}, },
"unimplementedWhiteList":[ "unimplementedWhiteList":[
1, 1,
......
...@@ -71,7 +71,10 @@ ...@@ -71,7 +71,10 @@
"hint":{ "hint":{
"waitingDuration":1.5, "waitingDuration":1.5,
"maxCount": 1 "maxCount": 1
} },
"commonDelay": 200,
"moveDelay": 500,
"chainingDelay": 800
}, },
"unimplementedWhiteList":[ "unimplementedWhiteList":[
1, 1,
......
This diff is collapsed.
...@@ -115,5 +115,9 @@ ...@@ -115,5 +115,9 @@
"112": { "112": {
"protoType": "attack_disable", "protoType": "attack_disable",
"fields": [] "fields": []
},
"73": {
"protoType": "chain_solved",
"fields": [{ "fieldName": "solved_index", "fieldType": "uint8"}]
} }
} }
...@@ -31,6 +31,7 @@ const MsgConstructorMap: Map<string, Constructor> = new Map([ ...@@ -31,6 +31,7 @@ const MsgConstructorMap: Map<string, Constructor> = new Map([
["chaining", ygopro.StocGameMessage.MsgChaining], ["chaining", ygopro.StocGameMessage.MsgChaining],
["attack", ygopro.StocGameMessage.MsgAttack], ["attack", ygopro.StocGameMessage.MsgAttack],
["attack_disable", ygopro.StocGameMessage.MsgAttackDisabled], ["attack_disable", ygopro.StocGameMessage.MsgAttackDisabled],
["chain_solved", ygopro.StocGameMessage.MsgChainSolved],
]); ]);
export interface penetrateType { export interface penetrateType {
......
import { ygopro } from "@/api";
export default (chainSolved: ygopro.StocGameMessage.MsgChainSolved) => {
console.log(chainSolved);
};
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchEsHintMeta } from "@/stores"; import { useConfig } from "@/config";
import { fetchEsHintMeta, matStore } from "@/stores";
export default (chaining: ygopro.StocGameMessage.MsgChaining) => { export default (chaining: ygopro.StocGameMessage.MsgChaining) => {
fetchEsHintMeta({ fetchEsHintMeta({
originMsg: "「[?]」被发动时", originMsg: "「[?]」被发动时",
cardID: chaining.code, cardID: chaining.code,
}); });
matStore.setChaining(chaining.location, chaining.code, true);
setTimeout(() => {
matStore.setChaining(chaining.location, chaining.code, false);
// TODO: set chained
}, useConfig().ui.chainingDelay);
}; };
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { useConfig } from "@/config";
import { matStore } from "@/stores"; import { matStore } from "@/stores";
import onMsgAttack from "./attack"; import onMsgAttack from "./attack";
import onMsgAttackDisable from "./attackDisable"; import onMsgAttackDisable from "./attackDisable";
import onMsgChaining from "./chaining"; import onMsgChaining from "./chaining";
import onMsgChainSolved from "./chainSolved";
import onMsgDraw from "./draw"; import onMsgDraw from "./draw";
import onMsgFilpSummoned from "./flipSummoned"; import onMsgFilpSummoned from "./flipSummoned";
import onMsgFlipSummoning from "./flipSummoning"; import onMsgFlipSummoning from "./flipSummoning";
...@@ -55,7 +57,7 @@ const ActiveList = [ ...@@ -55,7 +57,7 @@ const ActiveList = [
"select_yes_no", "select_yes_no",
]; ];
const TIME_GAP = 200; const NeosConfig = useConfig();
export default function handleGameMsg(pb: ygopro.YgoStocMsg) { export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
// 防止MSG更新太频繁,做下控频 // 防止MSG更新太频繁,做下控频
...@@ -68,6 +70,9 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) { ...@@ -68,6 +70,9 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
matStore.waiting = false; matStore.waiting = false;
} }
// 先重置`delay`
matStore.delay = NeosConfig.ui.commonDelay;
switch (msg.gameMsg) { switch (msg.gameMsg) {
case "start": { case "start": {
onMsgStart(msg.start); onMsgStart(msg.start);
...@@ -107,6 +112,8 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) { ...@@ -107,6 +112,8 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
case "move": { case "move": {
onMsgMove(msg.move); onMsgMove(msg.move);
matStore.delay = NeosConfig.ui.moveDelay + 500;
break; break;
} }
case "select_card": { case "select_card": {
...@@ -232,6 +239,13 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) { ...@@ -232,6 +239,13 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
case "chaining": { case "chaining": {
onMsgChaining(msg.chaining); onMsgChaining(msg.chaining);
matStore.delay += NeosConfig.ui.chainingDelay;
break;
}
case "chain_solved": {
onMsgChainSolved(msg.chain_solved);
break; break;
} }
case "summoning": { case "summoning": {
...@@ -273,5 +287,5 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) { ...@@ -273,5 +287,5 @@ export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
break; break;
} }
} }
}, TIME_GAP); }, matStore.delay);
} }
...@@ -3,10 +3,12 @@ import { v4 as v4uuid } from "uuid"; ...@@ -3,10 +3,12 @@ import { v4 as v4uuid } from "uuid";
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { fetchOverlayMeta, store } from "@/stores"; import { fetchOverlayMeta, store } from "@/stores";
type MsgMove = ygopro.StocGameMessage.MsgMove; type MsgMove = ygopro.StocGameMessage.MsgMove;
import { useConfig } from "@/config";
import { REASON_MATERIAL } from "../../common"; import { REASON_MATERIAL } from "../../common";
const { matStore } = store; const { matStore } = store;
const NeosConfig = useConfig();
const OVERLAY_STACK: { uuid: string; code: number; sequence: number }[] = []; const OVERLAY_STACK: { uuid: string; code: number; sequence: number }[] = [];
...@@ -81,7 +83,7 @@ export default (move: MsgMove) => { ...@@ -81,7 +83,7 @@ export default (move: MsgMove) => {
() => () =>
(matStore.in(to.location).of(to.controler)[to.sequence].focus = (matStore.in(to.location).of(to.controler)[to.sequence].focus =
false), false),
500 // TODO: use config NeosConfig.ui.moveDelay
); );
break; break;
} }
...@@ -114,7 +116,7 @@ export default (move: MsgMove) => { ...@@ -114,7 +116,7 @@ export default (move: MsgMove) => {
for (const hand of matStore.in(to.location).of(to.controler)) { for (const hand of matStore.in(to.location).of(to.controler)) {
hand.focus = false; hand.focus = false;
} }
}, 500); }, NeosConfig.ui.moveDelay);
} }
break; break;
} }
......
...@@ -51,6 +51,7 @@ function reloadDuelField( ...@@ -51,6 +51,7 @@ function reloadDuelField(
idleInteractivities: [], idleInteractivities: [],
counters: {}, counters: {},
focus: false, focus: false,
chaining: false,
reload: true, reload: true,
}; };
}); });
......
...@@ -40,6 +40,7 @@ export default (start: ygopro.StocGameMessage.MsgStart) => { ...@@ -40,6 +40,7 @@ export default (start: ygopro.StocGameMessage.MsgStart) => {
zone: ygopro.CardZone.DECK, zone: ygopro.CardZone.DECK,
}, },
focus: false, focus: false,
chaining: false,
counters: {}, counters: {},
idleInteractivities: [], idleInteractivities: [],
}); });
...@@ -57,6 +58,7 @@ export default (start: ygopro.StocGameMessage.MsgStart) => { ...@@ -57,6 +58,7 @@ export default (start: ygopro.StocGameMessage.MsgStart) => {
zone: ygopro.CardZone.DECK, zone: ygopro.CardZone.DECK,
}, },
focus: false, focus: false,
chaining: false,
counters: {}, counters: {},
idleInteractivities: [], idleInteractivities: [],
}); });
...@@ -75,6 +77,7 @@ export default (start: ygopro.StocGameMessage.MsgStart) => { ...@@ -75,6 +77,7 @@ export default (start: ygopro.StocGameMessage.MsgStart) => {
zone: ygopro.CardZone.EXTRA, zone: ygopro.CardZone.EXTRA,
}, },
focus: false, focus: false,
chaining: false,
counters: {}, counters: {},
idleInteractivities: [], idleInteractivities: [],
}); });
......
import { sendTimeConfirm, ygopro } from "@/api"; import { sendTimeConfirm, ygopro } from "@/api";
import { useConfig } from "@/config";
import { matStore } from "@/stores"; import { matStore } from "@/stores";
const TIME_GAP = 200; // TODO: use config
export default function handleTimeLimit(timeLimit: ygopro.StocTimeLimit) { export default function handleTimeLimit(timeLimit: ygopro.StocTimeLimit) {
setTimeout(() => { setTimeout(() => {
matStore.timeLimits.set(timeLimit.player, timeLimit.left_time); matStore.timeLimits.set(timeLimit.player, timeLimit.left_time);
sendTimeConfirm(); sendTimeConfirm();
}, TIME_GAP); }, useConfig().ui.commonDelay);
} }
...@@ -41,6 +41,7 @@ class CardArray extends Array<CardState> implements ArrayCardState { ...@@ -41,6 +41,7 @@ class CardArray extends Array<CardState> implements ArrayCardState {
position == undefined ? ygopro.CardPosition.FACEUP_ATTACK : position, position == undefined ? ygopro.CardPosition.FACEUP_ATTACK : position,
}, },
focus: focus ?? false, focus: focus ?? false,
chaining: false,
counters: {}, counters: {},
idleInteractivities: [], idleInteractivities: [],
}); });
...@@ -161,6 +162,7 @@ const genBlock = (zone: ygopro.CardZone, n: number) => ...@@ -161,6 +162,7 @@ const genBlock = (zone: ygopro.CardZone, n: number) =>
zone, zone,
}, },
focus: false, focus: false,
chaining: false,
idleInteractivities: [], idleInteractivities: [],
counters: {}, counters: {},
})); }));
...@@ -253,9 +255,24 @@ export const matStore: MatState = proxy<MatState>({ ...@@ -253,9 +255,24 @@ export const matStore: MatState = proxy<MatState>({
result: ygopro.StocGameMessage.MsgWin.ActionType.UNKNOWN, result: ygopro.StocGameMessage.MsgWin.ActionType.UNKNOWN,
waiting: false, waiting: false,
unimplemented: 0, unimplemented: 0,
delay: 0,
// methods // methods
in: getZone, in: getZone,
isMe, isMe,
setChaining(location, code, isChaining) {
const target = this.in(location.location).of(location.controler)[
location.sequence
];
target.chaining = isChaining;
if (target.occupant) {
target.occupant.id = code;
}
if (target.location.zone == ygopro.CardZone.HAND) {
target.location.position = isChaining
? ygopro.CardPosition.FACEUP_ATTACK
: ygopro.CardPosition.FACEDOWN_ATTACK;
}
},
}); });
// @ts-ignore 挂到全局,便于调试 // @ts-ignore 挂到全局,便于调试
......
...@@ -94,11 +94,21 @@ export interface MatState { ...@@ -94,11 +94,21 @@ export interface MatState {
unimplemented: number; // 未处理的`Message` unimplemented: number; // 未处理的`Message`
delay: number; // MSG处理的延迟时间,目的时为了让一些动画处理完后再开始处理下一个MSG
// >>> methods >>> // >>> methods >>>
/** 根据zone获取hands/masters/magics... */ /** 根据zone获取hands/masters/magics... */
in: (zone: ygopro.CardZone) => BothSide<DuelFieldState>; in: (zone: ygopro.CardZone) => BothSide<DuelFieldState>;
/** 根据自己的先后手判断是否是自己 */ /** 根据自己的先后手判断是否是自己 */
isMe: (player: number) => boolean; isMe: (player: number) => boolean;
// 添加连锁中状态
// - 当是手牌以外的卡时,修改code并设置chaining字段;
// - 当是手牌中的卡时,修改code,设置chaining字段,并修改position,参数`isChaining`为true时修改成`FaceUpAttack`,为false时修改成`FaceDownAttack`
setChaining: (
location: ygopro.CardLocation,
code: number,
isChaining: boolean
) => void;
} }
export interface InitInfo { export interface InitInfo {
...@@ -121,6 +131,7 @@ export interface CardState { ...@@ -121,6 +131,7 @@ export interface CardState {
position?: ygopro.CardPosition; // 卡片的姿势:攻击还是守备 position?: ygopro.CardPosition; // 卡片的姿势:攻击还是守备
}; // 位置信息,叫location的原因是为了和ygo对齐 }; // 位置信息,叫location的原因是为了和ygo对齐
focus: boolean; // 用于实现动画效果,当这个字段为true时,该张卡片会被放大并在屏幕中央展示 focus: boolean; // 用于实现动画效果,当这个字段为true时,该张卡片会被放大并在屏幕中央展示
chaining: boolean; // 是否在连锁中
idleInteractivities: Interactivity<number>[]; // IDLE状态下的互动信息 idleInteractivities: Interactivity<number>[]; // IDLE状态下的互动信息
placeInteractivity?: Interactivity<{ placeInteractivity?: Interactivity<{
controler: number; controler: number;
......
...@@ -25,8 +25,8 @@ export const Card: React.FC<{ ...@@ -25,8 +25,8 @@ export const Card: React.FC<{
facedown?: boolean; facedown?: boolean;
vertical?: boolean; vertical?: boolean;
highlight?: boolean; highlight?: boolean;
fly?: boolean;
focus?: boolean; focus?: boolean;
fly?: boolean;
transTime?: number; transTime?: number;
onClick?: MouseEventHandler<{}>; onClick?: MouseEventHandler<{}>;
style?: CSSProperties; style?: CSSProperties;
...@@ -40,8 +40,8 @@ export const Card: React.FC<{ ...@@ -40,8 +40,8 @@ export const Card: React.FC<{
opponent = false, opponent = false,
vertical = false, vertical = false,
highlight = false, highlight = false,
fly = false,
focus = false, focus = false,
fly = false,
transTime = 0.3, transTime = 0.3,
onClick, onClick,
style = {}, style = {},
...@@ -49,7 +49,7 @@ export const Card: React.FC<{ ...@@ -49,7 +49,7 @@ export const Card: React.FC<{
<div <div
className={classnames("card", { className={classnames("card", {
"card-defense": defense, "card-defense": defense,
fly, fly: fly && !focus,
})} })}
style={ style={
{ {
...@@ -59,7 +59,9 @@ export const Card: React.FC<{ ...@@ -59,7 +59,9 @@ export const Card: React.FC<{
"--shadow": hight > 0 ? 1 : 0, "--shadow": hight > 0 ? 1 : 0,
"--opponent-deg": opponent ? "180deg" : "0deg", "--opponent-deg": opponent ? "180deg" : "0deg",
"--vertical": vertical ? 1 : 0, "--vertical": vertical ? 1 : 0,
"--trans-time": `${transTime}s`, "--trans-time": `${
fly ? NeosConfig.ui.chainingDelay / 1000 : transTime
}s`,
"--highlight-on": highlight ? 1 : 0, "--highlight-on": highlight ? 1 : 0,
"--scale-focus": focus ? FOCUS_SCALE : 1, "--scale-focus": focus ? FOCUS_SCALE : 1,
"--card-img": facedown "--card-img": facedown
......
...@@ -103,7 +103,11 @@ export const Mat = () => { ...@@ -103,7 +103,11 @@ export const Mat = () => {
facedown={CardStateToFaceDown(card)} facedown={CardStateToFaceDown(card)}
vertical={card.location.zone == YgoZone.HAND || card.focus} vertical={card.location.zone == YgoZone.HAND || card.focus}
highlight={card.idleInteractivities.length > 0} highlight={card.idleInteractivities.length > 0}
focus={card.focus} focus={
card.focus ||
(card.chaining && card.location.zone == YgoZone.HAND)
}
fly={card.chaining && card.location.zone != YgoZone.HAND}
opponent={card.opponent} opponent={card.opponent}
onClick={ onClick={
card.location.zone == YgoZone.SZONE || card.location.zone == YgoZone.SZONE ||
...@@ -130,7 +134,7 @@ function cardStateToRow(state: RenderCard): number { ...@@ -130,7 +134,7 @@ function cardStateToRow(state: RenderCard): number {
case YgoZone.DECK: case YgoZone.DECK:
return 0; return 0;
case YgoZone.HAND: case YgoZone.HAND:
return -1; return state.chaining ? 2 : -1;
case YgoZone.SZONE: case YgoZone.SZONE:
return state.sequence >= 5 ? 1 : 0; return state.sequence >= 5 ? 1 : 0;
case YgoZone.GRAVE: case YgoZone.GRAVE:
...@@ -148,7 +152,7 @@ function cardStateToRow(state: RenderCard): number { ...@@ -148,7 +152,7 @@ function cardStateToRow(state: RenderCard): number {
case YgoZone.DECK: case YgoZone.DECK:
return 4; return 4;
case YgoZone.HAND: case YgoZone.HAND:
return 5; return state.chaining ? 2 : 5;
case YgoZone.SZONE: case YgoZone.SZONE:
return state.sequence >= 5 ? 3 : 4; return state.sequence >= 5 ? 3 : 4;
case YgoZone.GRAVE: case YgoZone.GRAVE:
...@@ -170,7 +174,7 @@ function cardStateToCol(state: RenderCard): number { ...@@ -170,7 +174,7 @@ function cardStateToCol(state: RenderCard): number {
case YgoZone.EXTRA: case YgoZone.EXTRA:
return 5; return 5;
case YgoZone.HAND: case YgoZone.HAND:
return 4 - state.sequence; return state.chaining ? 2 : 4 - state.sequence;
case YgoZone.SZONE: case YgoZone.SZONE:
return state.sequence >= 5 ? 5 : 4 - state.sequence; return state.sequence >= 5 ? 5 : 4 - state.sequence;
case YgoZone.DECK: case YgoZone.DECK:
...@@ -191,7 +195,7 @@ function cardStateToCol(state: RenderCard): number { ...@@ -191,7 +195,7 @@ function cardStateToCol(state: RenderCard): number {
case YgoZone.EXTRA: case YgoZone.EXTRA:
return -1; return -1;
case YgoZone.HAND: case YgoZone.HAND:
return state.sequence; return state.chaining ? 2 : state.sequence;
case YgoZone.SZONE: case YgoZone.SZONE:
return state.sequence >= 5 ? -1 : state.sequence; return state.sequence >= 5 ? -1 : state.sequence;
case YgoZone.DECK: case YgoZone.DECK:
......
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