Commit 6fc21e22 authored by Chunchi Che's avatar Chunchi Che

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

Feat/service/chain

See merge request !198
parents 7bc9f266 e37fe4ad
Pipeline #21747 passed with stages
in 15 minutes and 4 seconds
...@@ -49,20 +49,12 @@ export interface CardText { ...@@ -49,20 +49,12 @@ export interface CardText {
* @returns 卡片数据 * @returns 卡片数据
* *
* */ * */
export async function fetchCard( export async function fetchCard(id: number): Promise<CardMeta> {
id: number,
local: boolean = true
): Promise<CardMeta> {
if (local) {
const res = await sqliteMiddleWare({ const res = await sqliteMiddleWare({
cmd: sqliteCmd.SELECT, cmd: sqliteCmd.SELECT,
payload: { id }, payload: { id },
}); });
return res.selectResult ? res.selectResult : { id, data: {}, text: {} }; return res.selectResult ? res.selectResult : { id, data: {}, text: {} };
}
const res = await axios.get<CardMeta>("http://localhost:3030/cards/" + id);
return res.data;
} }
export function getCardStr(meta: CardMeta, idx: number): string | undefined { export function getCardStr(meta: CardMeta, idx: number): string | undefined {
......
...@@ -30,6 +30,6 @@ export async function getStrings(description: number): Promise<string> { ...@@ -30,6 +30,6 @@ export async function getStrings(description: number): Promise<string> {
const code = description >> 4; const code = description >> 4;
const index = description & 0xf; const index = description & 0xf;
return getCardStr(await fetchCard(code, true), index) || ""; return getCardStr(await fetchCard(code), index) || "";
} }
} }
// web平台上websocket的消息到达是保序的,但是不能保证对这些消息的逻辑处理是保序的。
// 现在我们有这样一个需求:需要保证每次只处理一个消息,在上一个消息处理完后,再进行下一个消息的处理。
//
// 因此封装了一个`WebSocketStream`类,当每次Websocket连接中有消息到达时,往流中添加event,
// 同时执行器会不断地从流中获取event进行处理。
import { sleep } from "./sleep"; import { sleep } from "./sleep";
const SLEEP_INTERVAL = 200; const SLEEP_INTERVAL = 200;
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { matStore } from "@/stores";
export default (chainSolved: ygopro.StocGameMessage.MsgChainSolved) => { export default (chainSolved: ygopro.StocGameMessage.MsgChainSolved) => {
console.log(chainSolved); const location = matStore.chains
.splice(chainSolved.solved_index - 1, 1)
.at(0);
if (location) {
// 设置被连锁状态为空
matStore.setChained(location, undefined);
} else {
console.warn("pop from chains return null!");
}
}; };
...@@ -9,9 +9,15 @@ export default async (chaining: ygopro.StocGameMessage.MsgChaining) => { ...@@ -9,9 +9,15 @@ export default async (chaining: ygopro.StocGameMessage.MsgChaining) => {
cardID: chaining.code, cardID: chaining.code,
}); });
matStore.setChaining(chaining.location, chaining.code, true); await matStore.setChaining(chaining.location, chaining.code, true);
await sleep(useConfig().ui.chainingDelay); await sleep(useConfig().ui.chainingDelay);
matStore.setChaining(chaining.location, chaining.code, false); const location = chaining.location;
// TODO: set chained
// 恢复成非`chaining`状态
await matStore.setChaining(location, chaining.code, false);
// 将`location`添加到连锁栈
matStore.chains.push(location);
// 设置被连锁状态
matStore.setChained(location, matStore.chains.length);
}; };
...@@ -22,6 +22,7 @@ export default async (move: MsgMove) => { ...@@ -22,6 +22,7 @@ export default async (move: MsgMove) => {
// FIXME: 考虑超量素材的情况 // FIXME: 考虑超量素材的情况
let uuid; let uuid;
let chainIndex;
switch (from.location) { switch (from.location) {
case ygopro.CardZone.MZONE: case ygopro.CardZone.MZONE:
case ygopro.CardZone.SZONE: { case ygopro.CardZone.SZONE: {
...@@ -32,6 +33,7 @@ export default async (move: MsgMove) => { ...@@ -32,6 +33,7 @@ export default async (move: MsgMove) => {
target.occupant = undefined; target.occupant = undefined;
target.overlay_materials = []; target.overlay_materials = [];
uuid = target.uuid; uuid = target.uuid;
chainIndex = target.chainIndex;
// 需要重新分配UUID // 需要重新分配UUID
target.uuid = v4uuid(); target.uuid = v4uuid();
break; break;
...@@ -47,6 +49,7 @@ export default async (move: MsgMove) => { ...@@ -47,6 +49,7 @@ export default async (move: MsgMove) => {
.of(from.controler) .of(from.controler)
.remove(from.sequence); .remove(from.sequence);
uuid = removed.uuid; uuid = removed.uuid;
chainIndex = removed.chainIndex;
break; break;
} }
...@@ -77,8 +80,12 @@ export default async (move: MsgMove) => { ...@@ -77,8 +80,12 @@ export default async (move: MsgMove) => {
.of(to.controler) .of(to.controler)
.setOccupant(to.sequence, code, to.position, true); .setOccupant(to.sequence, code, to.position, true);
if (uuid) { if (uuid) {
// 设置UUID
matStore.in(to.location).of(to.controler)[to.sequence].uuid = uuid; matStore.in(to.location).of(to.controler)[to.sequence].uuid = uuid;
} }
// 设置连锁序号
matStore.in(to.location).of(to.controler)[to.sequence].chainIndex =
chainIndex;
await sleep(NeosConfig.ui.moveDelay); await sleep(NeosConfig.ui.moveDelay);
matStore.in(to.location).of(to.controler)[to.sequence].focus = false; matStore.in(to.location).of(to.controler)[to.sequence].focus = false;
...@@ -92,7 +99,7 @@ export default async (move: MsgMove) => { ...@@ -92,7 +99,7 @@ export default async (move: MsgMove) => {
matStore matStore
.in(to.location) .in(to.location)
.of(to.controler) .of(to.controler)
.insert(uuid, code, to.sequence, to.position); .insert(uuid, code, to.sequence, to.position, false, chainIndex);
} }
break; break;
} }
...@@ -106,7 +113,8 @@ export default async (move: MsgMove) => { ...@@ -106,7 +113,8 @@ export default async (move: MsgMove) => {
code, code,
to.sequence, to.sequence,
ygopro.CardPosition.FACEUP_ATTACK, ygopro.CardPosition.FACEUP_ATTACK,
true true,
chainIndex
); );
await sleep(NeosConfig.ui.moveDelay); await sleep(NeosConfig.ui.moveDelay);
......
...@@ -6,7 +6,7 @@ type MsgSortCard = ygopro.StocGameMessage.MsgSortCard; ...@@ -6,7 +6,7 @@ type MsgSortCard = ygopro.StocGameMessage.MsgSortCard;
export default async (sortCard: MsgSortCard) => { export default async (sortCard: MsgSortCard) => {
await Promise.all( await Promise.all(
sortCard.options.map(async ({ code, response }) => { sortCard.options.map(async ({ code, response }) => {
const meta = await fetchCard(code!, true); const meta = await fetchCard(code!);
messageStore.sortCardModal.options.push({ messageStore.sortCardModal.options.push({
meta, meta,
response: response!, response: response!,
......
...@@ -21,7 +21,7 @@ export const fetchSelectHintMeta = async ({ ...@@ -21,7 +21,7 @@ export const fetchSelectHintMeta = async ({
let selectHintMeta = ""; let selectHintMeta = "";
if (selectHintData > DESCRIPTION_LIMIT) { if (selectHintData > DESCRIPTION_LIMIT) {
// 针对`MSG_SELECT_PLACE`的特化逻辑 // 针对`MSG_SELECT_PLACE`的特化逻辑
const cardMeta = await fetchCard(selectHintData, true); const cardMeta = await fetchCard(selectHintData);
selectHintMeta = fetchStrings("!system", 569).replace( selectHintMeta = fetchStrings("!system", 569).replace(
"[%ls]", "[%ls]",
cardMeta.text.name || "[?]" cardMeta.text.name || "[?]"
......
...@@ -31,10 +31,11 @@ class CardArray extends Array<CardState> implements ArrayCardState { ...@@ -31,10 +31,11 @@ class CardArray extends Array<CardState> implements ArrayCardState {
controller: number, controller: number,
id: number, id: number,
position?: ygopro.CardPosition, position?: ygopro.CardPosition,
focus?: boolean focus?: boolean,
chainIndex?: number
) => ({ ) => ({
uuid, uuid,
occupant: await fetchCard(id, true), occupant: await fetchCard(id),
location: { location: {
controler: controller, controler: controller,
zone: this.zone, zone: this.zone,
...@@ -43,6 +44,7 @@ class CardArray extends Array<CardState> implements ArrayCardState { ...@@ -43,6 +44,7 @@ class CardArray extends Array<CardState> implements ArrayCardState {
}, },
focus: focus ?? false, focus: focus ?? false,
chaining: false, chaining: false,
chainIndex,
directAttack: false, directAttack: false,
counters: {}, counters: {},
idleInteractivities: [], idleInteractivities: [],
...@@ -56,14 +58,16 @@ class CardArray extends Array<CardState> implements ArrayCardState { ...@@ -56,14 +58,16 @@ class CardArray extends Array<CardState> implements ArrayCardState {
id: number, id: number,
sequence: number, sequence: number,
position?: ygopro.CardPosition, position?: ygopro.CardPosition,
focus?: boolean focus?: boolean,
chainIndex?: number
) { ) {
const card = await this.genCard( const card = await this.genCard(
uuid, uuid,
this.getController(), this.getController(),
id, id,
position, position,
focus focus,
chainIndex
); );
this.splice(sequence, 0, card); this.splice(sequence, 0, card);
} }
...@@ -234,6 +238,8 @@ export const matStore: MatState = proxy<MatState>({ ...@@ -234,6 +238,8 @@ export const matStore: MatState = proxy<MatState>({
decks: genDuelCardArray([], DECK), decks: genDuelCardArray([], DECK),
extraDecks: genDuelCardArray([], EXTRA), extraDecks: genDuelCardArray([], EXTRA),
chains: [],
timeLimits: { timeLimits: {
// 时间限制 // 时间限制
me: -1, me: -1,
...@@ -262,15 +268,17 @@ export const matStore: MatState = proxy<MatState>({ ...@@ -262,15 +268,17 @@ export const matStore: MatState = proxy<MatState>({
// methods // methods
in: getZone, in: getZone,
isMe, isMe,
setChaining(location, code, isChaining) { async setChaining(location, code, isChaining) {
const target = this.in(location.location) const target = this.in(location.location)
.of(location.controler) .of(location.controler)
.at(location.sequence); .at(location.sequence);
if (target) { if (target) {
target.chaining = isChaining; target.chaining = isChaining;
if (target.occupant && isChaining) { if (target.occupant && isChaining) {
// 目前需要判断`isChaining`为ture才设置id,因为有些手坑发效果后会move到墓地,运行到这里的时候已经和原来的位置对不上了,这时候不设置id // 目前需要判断`isChaining`为ture才设置meta,因为有些手坑发效果后会move到墓地,
target.occupant.id = code; // 运行到这里的时候已经和原来的位置对不上了,这时候不设置meta
const meta = await fetchCard(code);
target.occupant = meta;
} }
if (target.location.zone == ygopro.CardZone.HAND) { if (target.location.zone == ygopro.CardZone.HAND) {
target.location.position = isChaining target.location.position = isChaining
...@@ -279,6 +287,14 @@ export const matStore: MatState = proxy<MatState>({ ...@@ -279,6 +287,14 @@ export const matStore: MatState = proxy<MatState>({
} }
} }
}, },
setChained(location, chainIndex) {
const target = this.in(location.location)
.of(location.controler)
.at(location.sequence);
if (target) {
target.chainIndex = chainIndex;
}
},
}); });
// @ts-ignore 挂到全局,便于调试 // @ts-ignore 挂到全局,便于调试
......
...@@ -21,7 +21,8 @@ export interface DuelFieldState extends Array<CardState> { ...@@ -21,7 +21,8 @@ export interface DuelFieldState extends Array<CardState> {
id: number, id: number,
sequence: number, sequence: number,
position?: ygopro.CardPosition, position?: ygopro.CardPosition,
focus?: boolean focus?: boolean,
chainIndex?: number
) => Promise<void>; ) => Promise<void>;
/** 在末尾添加卡片 */ /** 在末尾添加卡片 */
add: ( add: (
...@@ -78,6 +79,8 @@ export interface MatState { ...@@ -78,6 +79,8 @@ export interface MatState {
extraDecks: BothSide<ExtraDeckState>; // 双方的额外卡组状态 extraDecks: BothSide<ExtraDeckState>; // 双方的额外卡组状态
chains: ygopro.CardLocation[]; // 连锁的卡片位置
timeLimits: BothSide<number> & { timeLimits: BothSide<number> & {
set: (controller: number, time: number) => void; set: (controller: number, time: number) => void;
}; // 双方的时间限制 }; // 双方的时间限制
...@@ -108,7 +111,9 @@ export interface MatState { ...@@ -108,7 +111,9 @@ export interface MatState {
location: ygopro.CardLocation, location: ygopro.CardLocation,
code: number, code: number,
isChaining: boolean isChaining: boolean
) => void; ) => Promise<void>;
// 添加被连锁状态
setChained: (location: ygopro.CardLocation, chainIndex?: number) => void;
} }
export interface InitInfo { export interface InitInfo {
...@@ -132,6 +137,8 @@ export interface CardState { ...@@ -132,6 +137,8 @@ export interface CardState {
}; // 位置信息,叫location的原因是为了和ygo对齐 }; // 位置信息,叫location的原因是为了和ygo对齐
focus: boolean; // 用于实现动画效果,当这个字段为true时,该张卡片会被放大并在屏幕中央展示 focus: boolean; // 用于实现动画效果,当这个字段为true时,该张卡片会被放大并在屏幕中央展示
chaining: boolean; // 是否在连锁中 chaining: boolean; // 是否在连锁中
chainIndex?: number /*连锁的序号,如果为空表示不在连锁
TODO: 目前是妥协的设计,因为其实一张卡是可以在同一个连锁链中被连锁多次的,这里为了避免太过复杂只保存最后的连锁序号*/;
directAttack: boolean; // 是否正在直接攻击为玩家 directAttack: boolean; // 是否正在直接攻击为玩家
attackTarget?: CardState & { sequence: number; opponent: boolean }; // 攻击目标。(嵌套结构可行么?) attackTarget?: CardState & { sequence: number; opponent: boolean }; // 攻击目标。(嵌套结构可行么?)
idleInteractivities: Interactivity<number>[]; // IDLE状态下的互动信息 idleInteractivities: Interactivity<number>[]; // IDLE状态下的互动信息
......
...@@ -76,10 +76,6 @@ p { ...@@ -76,10 +76,6 @@ p {
} }
.container { .container {
// left: 50%;
// position: fixed;
// top: 50%;
// transform: translate(-50%, -50%);
margin: 0 auto; margin: 0 auto;
width: 100%; width: 100%;
max-width: 300px; max-width: 300px;
......
...@@ -48,6 +48,8 @@ export const CardListModal = () => { ...@@ -48,6 +48,8 @@ export const CardListModal = () => {
} }
onClick={() => { onClick={() => {
messageStore.cardModal.meta = item.meta; messageStore.cardModal.meta = item.meta;
messageStore.cardModal.interactivies = item.interactivies;
messageStore.cardModal.counters = [];
messageStore.cardModal.isOpen = true; messageStore.cardModal.isOpen = true;
}} }}
> >
......
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