Commit a47e1e93 authored by Chunchi Che's avatar Chunchi Che

Merge branch 'opt/move/focus' into 'main'

Opt/move/focus

See merge request mycard/Neos!184
parents 79611cf5 80edb50d
...@@ -55,216 +55,223 @@ const ActiveList = [ ...@@ -55,216 +55,223 @@ const ActiveList = [
"select_yes_no", "select_yes_no",
]; ];
export default function handleGameMsg(pb: ygopro.YgoStocMsg) { const TIME_GAP = 200;
const msg = pb.stoc_game_msg;
if (ActiveList.includes(msg.gameMsg)) {
matStore.waiting = false;
}
switch (msg.gameMsg) {
case "start": {
onMsgStart(msg.start);
break;
}
case "draw": {
onMsgDraw(msg.draw);
break;
}
case "new_turn": {
onMsgNewTurn(msg.new_turn);
break;
}
case "new_phase": {
onMsgNewPhase(msg.new_phase);
break;
}
case "hint": {
onMsgHint(msg.hint);
break;
}
case "select_idle_cmd": {
onMsgSelectIdleCmd(msg.select_idle_cmd);
break;
}
case "select_place": {
onMsgSelectPlace(msg.select_place);
break;
}
case "move": {
onMsgMove(msg.move);
break;
}
case "select_card": {
onMsgSelectCard(msg.select_card);
break;
}
case "select_chain": {
onMsgSelectChain(msg.select_chain);
break;
}
case "select_effect_yn": {
onMsgSelectEffectYn(msg.select_effect_yn);
break;
}
case "select_position": {
onMsgSelectPosition(msg.select_position);
break;
}
case "select_option": {
onMsgSelectOption(msg.select_option);
break;
}
case "shuffle_hand": {
onMsgShuffleHand(msg.shuffle_hand);
break;
}
case "select_battle_cmd": {
onMsgSelectBattleCmd(msg.select_battle_cmd);
break;
}
case "pos_change": {
onMsgPosChange(msg.pos_change);
break;
}
case "select_unselect_card": {
onMsgSelectUnselectCard(msg.select_unselect_card);
break;
}
case "select_yes_no": {
onMsgSelectYesNo(msg.select_yes_no);
break;
}
case "update_hp": {
onMsgUpdateHp(msg.update_hp);
break;
}
case "win": {
onMsgWin(msg.win);
break;
}
case "wait": {
onMsgWait(msg.wait);
break;
}
case "update_data": {
onMsgUpdateData(msg.update_data);
break;
}
case "reload_field": {
onMsgReloadField(msg.reload_field);
break;
}
case "select_sum": {
onMsgSelectSum(msg.select_sum);
break;
}
case "select_tribute": {
onMsgSelectTribute(msg.select_tribute);
break;
}
case "update_counter": {
onMsgUpdateCounter(msg.update_counter);
break;
}
case "select_counter": {
onMsgSelectCounter(msg.select_counter);
break;
}
case "sort_card": {
onMsgSortCard(msg.sort_card);
break;
}
case "set": {
onMsgSet(msg.set);
break;
}
case "swap": {
onMsgSwap(msg.swap);
break;
}
case "attack": {
onMsgAttack(msg.attack);
break; export default function handleGameMsg(pb: ygopro.YgoStocMsg) {
} // 防止MSG更新太频繁,做下控频
case "attack_disable": { //
onMsgAttackDisable(msg.attack_disable); // TODO: 细化需要控频的MSG
setTimeout(() => {
break; const msg = pb.stoc_game_msg;
}
case "chaining": { if (ActiveList.includes(msg.gameMsg)) {
onMsgChaining(msg.chaining); matStore.waiting = false;
}
break;
} switch (msg.gameMsg) {
case "summoning": { case "start": {
onMsgSummoning(msg.summoning); onMsgStart(msg.start);
break; break;
} }
case "summoned": { case "draw": {
onMsgSummoned(msg.summoned); onMsgDraw(msg.draw);
break; break;
} }
case "flip_summoning": { case "new_turn": {
onMsgFlipSummoning(msg.flip_summoning); onMsgNewTurn(msg.new_turn);
break; break;
} }
case "flip_summoned": { case "new_phase": {
onMsgFilpSummoned(msg.flip_summoned); onMsgNewPhase(msg.new_phase);
break; break;
} }
case "sp_summoning": { case "hint": {
onMsgSpSummoning(msg.sp_summoning); onMsgHint(msg.hint);
break; break;
} }
case "sp_summoned": { case "select_idle_cmd": {
onMsgSpSummoned(msg.sp_summoned); onMsgSelectIdleCmd(msg.select_idle_cmd);
break; break;
} }
case "unimplemented": { case "select_place": {
onUnimplemented(msg.unimplemented); onMsgSelectPlace(msg.select_place);
break; break;
} }
default: { case "move": {
break; onMsgMove(msg.move);
}
} break;
}
case "select_card": {
onMsgSelectCard(msg.select_card);
break;
}
case "select_chain": {
onMsgSelectChain(msg.select_chain);
break;
}
case "select_effect_yn": {
onMsgSelectEffectYn(msg.select_effect_yn);
break;
}
case "select_position": {
onMsgSelectPosition(msg.select_position);
break;
}
case "select_option": {
onMsgSelectOption(msg.select_option);
break;
}
case "shuffle_hand": {
onMsgShuffleHand(msg.shuffle_hand);
break;
}
case "select_battle_cmd": {
onMsgSelectBattleCmd(msg.select_battle_cmd);
break;
}
case "pos_change": {
onMsgPosChange(msg.pos_change);
break;
}
case "select_unselect_card": {
onMsgSelectUnselectCard(msg.select_unselect_card);
break;
}
case "select_yes_no": {
onMsgSelectYesNo(msg.select_yes_no);
break;
}
case "update_hp": {
onMsgUpdateHp(msg.update_hp);
break;
}
case "win": {
onMsgWin(msg.win);
break;
}
case "wait": {
onMsgWait(msg.wait);
break;
}
case "update_data": {
onMsgUpdateData(msg.update_data);
break;
}
case "reload_field": {
onMsgReloadField(msg.reload_field);
break;
}
case "select_sum": {
onMsgSelectSum(msg.select_sum);
break;
}
case "select_tribute": {
onMsgSelectTribute(msg.select_tribute);
break;
}
case "update_counter": {
onMsgUpdateCounter(msg.update_counter);
break;
}
case "select_counter": {
onMsgSelectCounter(msg.select_counter);
break;
}
case "sort_card": {
onMsgSortCard(msg.sort_card);
break;
}
case "set": {
onMsgSet(msg.set);
break;
}
case "swap": {
onMsgSwap(msg.swap);
break;
}
case "attack": {
onMsgAttack(msg.attack);
break;
}
case "attack_disable": {
onMsgAttackDisable(msg.attack_disable);
break;
}
case "chaining": {
onMsgChaining(msg.chaining);
break;
}
case "summoning": {
onMsgSummoning(msg.summoning);
break;
}
case "summoned": {
onMsgSummoned(msg.summoned);
break;
}
case "flip_summoning": {
onMsgFlipSummoning(msg.flip_summoning);
break;
}
case "flip_summoned": {
onMsgFilpSummoned(msg.flip_summoned);
break;
}
case "sp_summoning": {
onMsgSpSummoning(msg.sp_summoning);
break;
}
case "sp_summoned": {
onMsgSpSummoned(msg.sp_summoned);
break;
}
case "unimplemented": {
onUnimplemented(msg.unimplemented);
break;
}
default: {
break;
}
}
}, TIME_GAP);
} }
...@@ -72,10 +72,17 @@ export default (move: MsgMove) => { ...@@ -72,10 +72,17 @@ export default (move: MsgMove) => {
matStore matStore
.in(to.location) .in(to.location)
.of(to.controler) .of(to.controler)
.setOccupant(to.sequence, code, to.position); .setOccupant(to.sequence, code, to.position, true);
if (uuid) { if (uuid) {
matStore.in(to.location).of(to.controler)[to.sequence].uuid = uuid; matStore.in(to.location).of(to.controler)[to.sequence].uuid = uuid;
} }
setTimeout(
() =>
(matStore.in(to.location).of(to.controler)[to.sequence].focus =
false),
500 // use config
);
break; break;
} }
case ygopro.CardZone.REMOVED: case ygopro.CardZone.REMOVED:
...@@ -94,7 +101,7 @@ export default (move: MsgMove) => { ...@@ -94,7 +101,7 @@ export default (move: MsgMove) => {
matStore matStore
.in(to.location) .in(to.location)
.of(to.controler) .of(to.controler)
.insert(uuid, code, to.sequence); .insert(uuid, code, to.sequence, ygopro.CardPosition.FACEUP_ATTACK);
} }
break; break;
} }
......
import { sendTimeConfirm, ygopro } from "@/api"; import { sendTimeConfirm, ygopro } from "@/api";
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) {
matStore.timeLimits.set(timeLimit.player, timeLimit.left_time); setTimeout(() => {
sendTimeConfirm(); matStore.timeLimits.set(timeLimit.player, timeLimit.left_time);
sendTimeConfirm();
}, TIME_GAP);
} }
...@@ -29,7 +29,8 @@ class CardArray extends Array<CardState> implements ArrayCardState { ...@@ -29,7 +29,8 @@ class CardArray extends Array<CardState> implements ArrayCardState {
uuid: string, uuid: string,
controller: number, controller: number,
id: number, id: number,
position?: ygopro.CardPosition position?: ygopro.CardPosition,
focus?: boolean
) => ({ ) => ({
uuid, uuid,
occupant: await fetchCard(id, true), occupant: await fetchCard(id, true),
...@@ -39,6 +40,7 @@ class CardArray extends Array<CardState> implements ArrayCardState { ...@@ -39,6 +40,7 @@ class CardArray extends Array<CardState> implements ArrayCardState {
position: position:
position == undefined ? ygopro.CardPosition.FACEUP_ATTACK : position, position == undefined ? ygopro.CardPosition.FACEUP_ATTACK : position,
}, },
focus,
counters: {}, counters: {},
idleInteractivities: [], idleInteractivities: [],
}); });
...@@ -50,18 +52,26 @@ class CardArray extends Array<CardState> implements ArrayCardState { ...@@ -50,18 +52,26 @@ class CardArray extends Array<CardState> implements ArrayCardState {
uuid: string, uuid: string,
id: number, id: number,
sequence: number, sequence: number,
position?: ygopro.CardPosition position?: ygopro.CardPosition,
focus?: boolean
) { ) {
const card = await this.genCard(uuid, this.getController(), id, position); const card = await this.genCard(
uuid,
this.getController(),
id,
position,
focus
);
this.splice(sequence, 0, card); this.splice(sequence, 0, card);
} }
async add( async add(
data: { uuid: string; id: number }[], data: { uuid: string; id: number }[],
position?: ygopro.CardPosition position?: ygopro.CardPosition,
focus?: boolean
) { ) {
const cards = await Promise.all( const cards = await Promise.all(
data.map(async ({ uuid, id }) => data.map(async ({ uuid, id }) =>
this.genCard(uuid, this.getController(), id, position) this.genCard(uuid, this.getController(), id, position, focus)
) )
); );
this.splice(this.length, 0, ...cards); this.splice(this.length, 0, ...cards);
...@@ -69,10 +79,12 @@ class CardArray extends Array<CardState> implements ArrayCardState { ...@@ -69,10 +79,12 @@ class CardArray extends Array<CardState> implements ArrayCardState {
async setOccupant( async setOccupant(
sequence: number, sequence: number,
id: number, id: number,
position?: ygopro.CardPosition position?: ygopro.CardPosition,
focus?: boolean
) { ) {
const meta = await fetchCard(id); const meta = await fetchCard(id);
const target = this[sequence]; const target = this[sequence];
target.focus = focus;
target.occupant = meta; target.occupant = meta;
if (position) { if (position) {
target.location.position = position; target.location.position = position;
......
...@@ -20,18 +20,21 @@ export interface DuelFieldState extends Array<CardState> { ...@@ -20,18 +20,21 @@ export interface DuelFieldState extends Array<CardState> {
uuid: string, uuid: string,
id: number, id: number,
sequence: number, sequence: number,
position?: ygopro.CardPosition position?: ygopro.CardPosition,
focus?: boolean
) => Promise<void>; ) => Promise<void>;
/** 在末尾添加卡片 */ /** 在末尾添加卡片 */
add: ( add: (
data: { uuid: string; id: number }[], data: { uuid: string; id: number }[],
position?: ygopro.CardPosition position?: ygopro.CardPosition,
focus?: boolean
) => Promise<void>; ) => Promise<void>;
/** 设置占据这个位置的卡片信息 */ /** 设置占据这个位置的卡片信息 */
setOccupant: ( setOccupant: (
sequence: number, sequence: number,
id: number, id: number,
position?: ygopro.CardPosition position?: ygopro.CardPosition,
focus?: boolean
) => Promise<void>; ) => Promise<void>;
/** 添加 idle 的交互性 */ /** 添加 idle 的交互性 */
addIdleInteractivity: ( addIdleInteractivity: (
...@@ -117,6 +120,7 @@ export interface CardState { ...@@ -117,6 +120,7 @@ export interface CardState {
zone: ygopro.CardZone; // 怪兽区/魔法陷阱区/手牌/卡组/墓地/除外区 zone: ygopro.CardZone; // 怪兽区/魔法陷阱区/手牌/卡组/墓地/除外区
position?: ygopro.CardPosition; // 卡片的姿势:攻击还是守备 position?: ygopro.CardPosition; // 卡片的姿势:攻击还是守备
}; // 位置信息,叫location的原因是为了和ygo对齐 }; // 位置信息,叫location的原因是为了和ygo对齐
focus?: boolean;
idleInteractivities: Interactivity<number>[]; // IDLE状态下的互动信息 idleInteractivities: Interactivity<number>[]; // IDLE状态下的互动信息
placeInteractivity?: Interactivity<{ placeInteractivity?: Interactivity<{
controler: number; controler: number;
......
...@@ -100,7 +100,7 @@ button:focus-visible { ...@@ -100,7 +100,7 @@ button:focus-visible {
--x: calc(var(--x-margin-left) + var(--x-padding)); --x: calc(var(--x-margin-left) + var(--x-padding));
--y: calc(var(--r) * calc(var(--block-height) + var(--block-row-gap))); --y: calc(var(--r) * calc(var(--block-height) + var(--block-row-gap)));
--z: calc(var(--h) * 1px); --z: calc(var(--h) * 1px);
transform: translateZ(var(--z)) rotateX(calc(var(--hand-rotate) * var(--vertical))); transform: translateZ(var(--z)) rotateX(calc(var(--hand-rotate) * var(--vertical))) scale(var(--scale-focus));
translate: var(--x) var(--y); translate: var(--x) var(--y);
rotate: calc(var(--opponent-deg) * (1 - var(--vertical))); rotate: calc(var(--opponent-deg) * (1 - var(--vertical)));
transform-style: preserve-3d; transform-style: preserve-3d;
......
...@@ -12,6 +12,9 @@ const ASSETS_BASE = ...@@ -12,6 +12,9 @@ const ASSETS_BASE =
? NeosConfig.assetsPath ? NeosConfig.assetsPath
: import.meta.env.BASE_URL + NeosConfig.assetsPath; : import.meta.env.BASE_URL + NeosConfig.assetsPath;
const FOCUS_SCALE = 2.5;
const FOCUS_HIGHT = 100;
export const Card: React.FC<{ export const Card: React.FC<{
code: number; code: number;
row: number; row: number;
...@@ -23,6 +26,7 @@ export const Card: React.FC<{ ...@@ -23,6 +26,7 @@ export const Card: React.FC<{
vertical?: boolean; vertical?: boolean;
highlight?: boolean; highlight?: boolean;
fly?: boolean; fly?: boolean;
focus?: boolean;
transTime?: number; transTime?: number;
onClick?: MouseEventHandler<{}>; onClick?: MouseEventHandler<{}>;
style?: CSSProperties; style?: CSSProperties;
...@@ -37,6 +41,7 @@ export const Card: React.FC<{ ...@@ -37,6 +41,7 @@ export const Card: React.FC<{
vertical = false, vertical = false,
highlight = false, highlight = false,
fly = false, fly = false,
focus = false,
transTime = 0.3, transTime = 0.3,
onClick, onClick,
style = {}, style = {},
...@@ -48,7 +53,7 @@ export const Card: React.FC<{ ...@@ -48,7 +53,7 @@ export const Card: React.FC<{
})} })}
style={ style={
{ {
"--h": hight, "--h": focus ? FOCUS_HIGHT : hight,
"--r": row, "--r": row,
"--c": col, "--c": col,
"--shadow": hight > 0 ? 1 : 0, "--shadow": hight > 0 ? 1 : 0,
...@@ -56,6 +61,7 @@ export const Card: React.FC<{ ...@@ -56,6 +61,7 @@ export const Card: React.FC<{
"--vertical": vertical ? 1 : 0, "--vertical": vertical ? 1 : 0,
"--trans-time": `${transTime}s`, "--trans-time": `${transTime}s`,
"--highlight-on": highlight ? 1 : 0, "--highlight-on": highlight ? 1 : 0,
"--scale-focus": focus ? FOCUS_SCALE : 1,
"--card-img": facedown "--card-img": facedown
? `url(${ASSETS_BASE + "/card_back.jpg"})` ? `url(${ASSETS_BASE + "/card_back.jpg"})`
: `url(${NeosConfig.cardImgUrl + "/" + code + ".jpg"})`, : `url(${NeosConfig.cardImgUrl + "/" + code + ".jpg"})`,
......
...@@ -95,13 +95,15 @@ export const Mat = () => { ...@@ -95,13 +95,15 @@ export const Mat = () => {
col={cardStateToCol(card)} col={cardStateToCol(card)}
hight={CardStateToHigh(card)} hight={CardStateToHigh(card)}
defense={ defense={
card.location.position === YgoPosition.DEFENSE || !card.focus &&
card.location.position === YgoPosition.FACEDOWN_DEFENSE || (card.location.position === YgoPosition.DEFENSE ||
card.location.position === YgoPosition.FACEUP_DEFENSE card.location.position === YgoPosition.FACEDOWN_DEFENSE ||
card.location.position === YgoPosition.FACEUP_DEFENSE)
} }
facedown={CardStateToFaceDown(card)} facedown={CardStateToFaceDown(card)}
vertical={card.location.zone == YgoZone.HAND} vertical={card.location.zone == YgoZone.HAND || card.focus}
highlight={card.idleInteractivities.length > 0} highlight={card.idleInteractivities.length > 0}
focus={card.focus}
opponent={card.opponent} opponent={card.opponent}
onClick={ onClick={
card.location.zone == YgoZone.SZONE || card.location.zone == YgoZone.SZONE ||
...@@ -121,6 +123,7 @@ export const Mat = () => { ...@@ -121,6 +123,7 @@ export const Mat = () => {
}; };
function cardStateToRow(state: RenderCard): number { function cardStateToRow(state: RenderCard): number {
if (state.focus) return 2;
if (state.opponent) { if (state.opponent) {
switch (state.location.zone) { switch (state.location.zone) {
case YgoZone.EXTRA: case YgoZone.EXTRA:
...@@ -161,6 +164,7 @@ function cardStateToRow(state: RenderCard): number { ...@@ -161,6 +164,7 @@ function cardStateToRow(state: RenderCard): number {
} }
function cardStateToCol(state: RenderCard): number { function cardStateToCol(state: RenderCard): number {
if (state.focus) return 2;
if (state.opponent) { if (state.opponent) {
switch (state.location.zone) { switch (state.location.zone) {
case YgoZone.EXTRA: case YgoZone.EXTRA:
...@@ -223,6 +227,7 @@ function CardStateToHigh(state: RenderCard): number { ...@@ -223,6 +227,7 @@ function CardStateToHigh(state: RenderCard): number {
} }
function CardStateToFaceDown(state: RenderCard): boolean { function CardStateToFaceDown(state: RenderCard): boolean {
if (state.focus && state.occupant?.id !== 0) return false;
const position = state.location.position; const position = state.location.position;
return ( return (
......
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