Add the interaction half of the sandbox: a held part and dialog stack as UI state outside the game store, with pure helpers (interactionsFor, dropPaths, pickPath, partFacings, nextFacing). PartPlacement is now interactive (pick up, drag to move, click to cycle facing), and a drop on a path with a stack-dialog interaction opens the deck dialog instead of moving directly. DialogLayer renders the stack-inspector dialog whose insert button issues a move. Add the dialog role, Setup.interactions, and Package.dialogs to @tts/bgm, and declare the deck insert dialog in games/poker.
163 lines
4.2 KiB
Markdown
163 lines
4.2 KiB
Markdown
# Poker
|
||
|
||
A standard 52-card poker deck, laid out on a table with a draw pile and
|
||
community-card slots. Exercises the bgm loader's `$variants` expansion to
|
||
generate a full deck from a single part definition.
|
||
|
||
```yaml role=package
|
||
id: poker
|
||
title: Poker
|
||
designer: Public Domain
|
||
players: 9
|
||
language: en
|
||
include: ['./**/*.yaml']
|
||
```
|
||
|
||
## Parts
|
||
|
||
A single `card` part expanded into 52 cards by the `$variants` CSV. Each row
|
||
picks a cell from the `13×4` face sheet (`cards-13x4.jpg`) — 13 ranks across,
|
||
4 suits down. All cards share the same back from the `4×1` back sheet
|
||
(`back-4x1.png`).
|
||
|
||
```yaml role=part.card
|
||
face: ./cards-13x4.jpg
|
||
back: ./back-4x1.png
|
||
size: [63, 88, 0.3]
|
||
fillet: 2
|
||
$variants: ./cards.csv
|
||
```
|
||
|
||
```csv file=cards.csv
|
||
id,rank,suit,faceCrop,backCrop
|
||
string,string,string,[number;number;number;number],[number;number;number;number]
|
||
2s,2,spades,[0;0;13;4],[0;0;4;1]
|
||
3s,3,spades,[1;0;13;4],[0;0;4;1]
|
||
4s,4,spades,[2;0;13;4],[0;0;4;1]
|
||
5s,5,spades,[3;0;13;4],[0;0;4;1]
|
||
6s,6,spades,[4;0;13;4],[0;0;4;1]
|
||
7s,7,spades,[5;0;13;4],[0;0;4;1]
|
||
8s,8,spades,[6;0;13;4],[0;0;4;1]
|
||
9s,9,spades,[7;0;13;4],[0;0;4;1]
|
||
10s,10,spades,[8;0;13;4],[0;0;4;1]
|
||
js,J,spades,[9;0;13;4],[0;0;4;1]
|
||
qs,Q,spades,[10;0;13;4],[0;0;4;1]
|
||
ks,K,spades,[11;0;13;4],[0;0;4;1]
|
||
as,A,spades,[12;0;13;4],[0;0;4;1]
|
||
2h,2,hearts,[0;1;13;4],[0;0;4;1]
|
||
3h,3,hearts,[1;1;13;4],[0;0;4;1]
|
||
4h,4,hearts,[2;1;13;4],[0;0;4;1]
|
||
5h,5,hearts,[3;1;13;4],[0;0;4;1]
|
||
6h,6,hearts,[4;1;13;4],[0;0;4;1]
|
||
7h,7,hearts,[5;1;13;4],[0;0;4;1]
|
||
8h,8,hearts,[6;1;13;4],[0;0;4;1]
|
||
9h,9,hearts,[7;1;13;4],[0;0;4;1]
|
||
10h,10,hearts,[8;1;13;4],[0;0;4;1]
|
||
jh,J,hearts,[9;1;13;4],[0;0;4;1]
|
||
qh,Q,hearts,[10;1;13;4],[0;0;4;1]
|
||
kh,K,hearts,[11;1;13;4],[0;0;4;1]
|
||
ah,A,hearts,[12;1;13;4],[0;0;4;1]
|
||
2d,2,diamonds,[0;2;13;4],[0;0;4;1]
|
||
3d,3,diamonds,[1;2;13;4],[0;0;4;1]
|
||
4d,4,diamonds,[2;2;13;4],[0;0;4;1]
|
||
5d,5,diamonds,[3;2;13;4],[0;0;4;1]
|
||
6d,6,diamonds,[4;2;13;4],[0;0;4;1]
|
||
7d,7,diamonds,[5;2;13;4],[0;0;4;1]
|
||
8d,8,diamonds,[6;2;13;4],[0;0;4;1]
|
||
9d,9,diamonds,[7;2;13;4],[0;0;4;1]
|
||
10d,10,diamonds,[8;2;13;4],[0;0;4;1]
|
||
jd,J,diamonds,[9;2;13;4],[0;0;4;1]
|
||
qd,Q,diamonds,[10;2;13;4],[0;0;4;1]
|
||
kd,K,diamonds,[11;2;13;4],[0;0;4;1]
|
||
ad,A,diamonds,[12;2;13;4],[0;0;4;1]
|
||
2c,2,clubs,[0;3;13;4],[0;0;4;1]
|
||
3c,3,clubs,[1;3;13;4],[0;0;4;1]
|
||
4c,4,clubs,[2;3;13;4],[0;0;4;1]
|
||
5c,5,clubs,[3;3;13;4],[0;0;4;1]
|
||
6c,6,clubs,[4;3;13;4],[0;0;4;1]
|
||
7c,7,clubs,[5;3;13;4],[0;0;4;1]
|
||
8c,8,clubs,[6;3;13;4],[0;0;4;1]
|
||
9c,9,clubs,[7;3;13;4],[0;0;4;1]
|
||
10c,10,clubs,[8;3;13;4],[0;0;4;1]
|
||
jc,J,clubs,[9;3;13;4],[0;0;4;1]
|
||
qc,Q,clubs,[10;3;13;4],[0;0;4;1]
|
||
kc,K,clubs,[11;3;13;4],[0;0;4;1]
|
||
ac,A,clubs,[12;3;13;4],[0;0;4;1]
|
||
```
|
||
|
||
## Board
|
||
|
||
A table with a draw pile on the left and five community-card slots across the
|
||
middle. The deck pile fans its stacked cards along a curve.
|
||
|
||
```yaml role=surface.board
|
||
id: poker
|
||
size: [600, 400]
|
||
layout:
|
||
- route: /deck
|
||
x: -250
|
||
y: 0
|
||
rotation: 0
|
||
stacking:
|
||
align: center
|
||
zStart: 0
|
||
#zEnd: 100
|
||
curve: M -50 -200 C 50 -150 450 -150 550 -200
|
||
- route: /community/:slot
|
||
candidates:
|
||
$variants: ./community.csv
|
||
```
|
||
|
||
```csv file=community.csv
|
||
slot,x,y,rotation
|
||
string,number,number,number
|
||
0,-100,0,0
|
||
1,-50,0,0
|
||
2,0,0,0
|
||
3,50,0,0
|
||
4,100,0,0
|
||
```
|
||
|
||
## Setup
|
||
|
||
Deal the whole deck facedown onto the draw pile (`poker:card` expands to every
|
||
card of that type, `facing: back`), then flip a flop onto the community slots
|
||
and stand a couple of drawn cards on their bottom edge.
|
||
|
||
```yaml role=setup.game
|
||
id: main
|
||
setup:
|
||
- path: /deck
|
||
parts: poker:card
|
||
facing: back
|
||
- path: /community/0
|
||
parts: poker:card#as
|
||
- path: /community/1
|
||
parts: poker:card#kh
|
||
- path: /community/2
|
||
parts: poker:card#7d
|
||
- path: /community/3
|
||
parts: poker:card#jc
|
||
facing: standing
|
||
- path: /community/4
|
||
parts: poker:card#2h
|
||
facing: standing
|
||
interactions:
|
||
- dialog: prompt#insert
|
||
on: [/deck]
|
||
```
|
||
|
||
## Dialog
|
||
|
||
Inserting a card into the middle of the deck is a compound interaction: the
|
||
deck is lifted into a stack-inspector dialog with an insertion cursor. The
|
||
cursor *is* the index — `move(id, /deck, index)` is discovered by scrolling,
|
||
not typed.
|
||
|
||
```yaml role=dialog.prompt
|
||
id: insert
|
||
title: Insert into deck
|
||
body: Scroll to the insertion point, then insert the held card.
|
||
widget: stack
|
||
```
|