Hostitel prida na volne sedadlo bota (+ Bot = heuristika, + AI bot = natrenovana siet) alebo bota odoberie; boti maju vlastnu ikonu a flag is_bot v rosteroch. Surove ucty bot:<kind>-<n> sa vsade zobrazuju cez displayName ako "Bot 2" / "AI bot 1" (stol, kopka, tabulky, historia). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
import type { PlayerInfo, StashData } from '../types';
|
|
import CardView from './CardView';
|
|
import { displayName } from '../lib/names';
|
|
|
|
interface Props {
|
|
stash: StashData | null;
|
|
players: PlayerInfo[];
|
|
myOrder: number;
|
|
}
|
|
|
|
const ROTATIONS = [-3, 2, -1, 1];
|
|
// Entry direction by seat offset from me: 0=me(bottom) 1=left 2=top 3=right.
|
|
const FLY_BY_OFFSET = ['fly-bottom', 'fly-left', 'fly-top', 'fly-right'];
|
|
|
|
export default function Trick({ stash, players, myOrder }: Props) {
|
|
// Seat order, starting from whoever led the trick.
|
|
const playOrder = stash
|
|
? [0, 1, 2, 3].map((i) => (stash.first_player + i) % 4)
|
|
: [];
|
|
|
|
const nameFor = (order: number) =>
|
|
displayName(players.find((p) => p.order === order)?.name);
|
|
|
|
const overlap = -16;
|
|
const slotH = 80;
|
|
|
|
if (!stash) {
|
|
return <div className="flex items-center justify-center" style={{ minHeight: slotH + 14 }} />;
|
|
}
|
|
|
|
return (
|
|
<div className="flex items-center justify-center">
|
|
{playOrder.map((order, i) => {
|
|
const card = stash.cards[String(order)];
|
|
// Only render cards that have actually been played — no placeholder slot
|
|
// for players still to play this trick.
|
|
if (!card) return null;
|
|
const offset = (order - myOrder + 4) % 4;
|
|
return (
|
|
<div
|
|
key={order}
|
|
className="relative flex flex-col items-center"
|
|
style={{ marginLeft: i === 0 ? 0 : overlap, zIndex: i + 1 }}
|
|
>
|
|
<span
|
|
className="uppercase text-center"
|
|
style={{
|
|
fontSize: 8,
|
|
letterSpacing: '.05em',
|
|
marginBottom: 3,
|
|
color: 'rgba(216,203,166,.72)',
|
|
}}
|
|
>
|
|
{nameFor(order)}
|
|
</span>
|
|
{/* Outer: flies in from the player's direction. Inner: static rotation. */}
|
|
<div style={{ animation: `${FLY_BY_OFFSET[offset]} .42s cubic-bezier(.2,.7,.3,1) both` }}>
|
|
<div style={{ transform: `rotate(${ROTATIONS[i]}deg)` }}>
|
|
<CardView card={card} size="md" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|