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