46 lines
4.1 KiB
TypeScript
46 lines
4.1 KiB
TypeScript
/** 도블 [?] "지금 할 수 있는 것" (docs/07 §5). Pure: only reads the client view. */
|
|
import type { DobbleView, MiniGame } from '@bg/games/dobble';
|
|
import type { PublicUser } from '@bg/shared/lite';
|
|
|
|
const MINI_KO: Record<MiniGame, string> = { tower: '탑 쌓기', well: '우물', hotPotato: '뜨거운 감자', poisonedGift: '독이 든 선물' };
|
|
|
|
/** What to compare, what a find does, how to win (§5.2). */
|
|
const GOAL: Record<MiniGame, string> = {
|
|
tower: '가운데 카드와 내 카드에 똑같이 있는 그림 하나를 찾아 눌러요. 먼저 찾으면 가운데 카드를 내 탑으로 가져와요. 카드를 가장 많이 모으면 1등이에요.',
|
|
well: '가운데 카드와 내 카드에 똑같이 있는 그림 하나를 찾아 눌러요. 먼저 찾으면 내 카드를 가운데에 내려놓아요. 내 카드를 먼저 다 비우면 1등이에요.',
|
|
hotPotato:
|
|
'내 카드와 다른 사람 카드에 똑같이 있는 그림 하나를 찾아 눌러요. 먼저 찾으면 내 카드 묶음을 그 사람에게 넘기고 이번 라운드는 빠져요. 마지막까지 카드를 들고 있으면 그 카드가 벌점이에요.',
|
|
poisonedGift: '가운데 카드와 다른 사람 카드에 똑같이 있는 그림 하나를 찾아 눌러요. 먼저 찾으면 가운데 카드를 그 사람에게 줘요. 카드를 가장 적게 받으면 1등이에요.',
|
|
};
|
|
|
|
export function helpNow(v: DobbleView, me: string | null, players: Record<string, PublicUser>): string {
|
|
const nick = (id: string) => players[id]?.nickname ?? '다른 사람';
|
|
const mode = v.options.miniGame;
|
|
const name = MINI_KO[mode];
|
|
if (v.phase === 'ended') return `게임이 끝났어요. ${v.result?.summary ?? ''}`.trim() + '\n최종 순위에서 각자 카드 수와 찾은 횟수를 볼 수 있어요.';
|
|
const round = mode === 'hotPotato' ? ` (${v.round}/${v.rounds}라운드)` : '';
|
|
if (v.phase === 'countdown') return `곧 카드가 공개돼요. 이번 게임은 '${name}'${round}이에요. 차례 없이 모두 동시에 찾아요.\n${GOAL[mode]}`;
|
|
if (v.phase === 'roundEnd') {
|
|
const r = v.lastRound;
|
|
const who = r?.loser ? (r.loser === me ? '내가' : `${nick(r.loser)}님이`) : null;
|
|
const what = r && who ? `${r.round}라운드에서 ${who} 카드 ${r.cards}장을 벌점으로 받았어요. ` : '';
|
|
return `라운드가 끝났어요. ${what}잠시 뒤 다음 라운드가 시작돼요.`;
|
|
}
|
|
const mine = v.seats.find((s) => s.id === me) ?? null;
|
|
if (!mine) return `'${name}' 게임 중이에요${round}. 모두 동시에 두 카드의 같은 그림을 찾고 있어요.\n${GOAL[mode]}`;
|
|
if (mine.status === 'finished') return `내 카드를 다 비웠어요!${mine.place ? ` ${mine.place}등이에요.` : ''} 남은 사람들의 게임을 지켜봐요.`;
|
|
if (mine.status === 'escaped') return `이번 라운드는 탈출했어요${round}. 남은 사람들이 카드를 넘기는 걸 지켜보고 다음 라운드를 기다려요.`;
|
|
if (mine.status === 'forfeit' || mine.status === 'left') return '게임에서 빠졌어요. 남은 사람들의 게임을 지켜봐요.';
|
|
if (v.pairs.length === 0) return `지금은 비교할 카드가 없어요. 잠시 기다려 주세요.\n${GOAL[mode]}`;
|
|
const lines = [`'${name}'${round} 게임 중이에요. 차례 없이 모두 동시에 찾아요. 어느 두 카드든 똑같은 그림은 딱 하나예요.`, GOAL[mode]];
|
|
lines.push(`두 카드 중 아무 쪽에서나 그 그림을 누르면 돼요.${v.options.symbolStyle === 'classic' ? ' 크기와 방향이 달라도 같은 그림이에요.' : ''}`);
|
|
if ((mode === 'hotPotato' || mode === 'poisonedGift') && v.pairs.length > 1) {
|
|
lines.push(mode === 'poisonedGift' ? '위쪽 사람 목록에서 선물을 줄 사람을 먼저 골라요.' : '위쪽 사람 목록에서 카드를 넘길 사람을 먼저 골라요.');
|
|
}
|
|
const o = v.options;
|
|
const lock = o.wrongLockoutMs ? `틀린 그림을 누르면 ${o.wrongLockoutMs / 1000}초 동안 못 눌러요.` : '이 방은 틀려도 잠기지 않아요.';
|
|
const give = mode === 'tower' && o.wrongGivesCard ? ' 틀리면 내 탑 맨 위 카드도 1장 돌려줘요.' : '';
|
|
lines.push(lock + give);
|
|
return lines.join('\n');
|
|
}
|