collin/mahjong · d5af31aa
Buttons are tiles, and the wall is a ladder
Collin Richards · 2026-08-25 08:13 UTC · d5af31aa6402b7f6acea8dcf81a2794ae24c5319 · parent bf80fd25 · browse files
modifiedsrc/game/wall.ts+10 −4
| ⋯ 154 unchanged lines | |||
| 155 | 155 | * `ui/WallRing` is for. `x` is across the table and `y` down it, both in tiles | |
| 156 | 156 | * so the placement carries to any window. Nought is the wall left where it was | |
| 157 | 157 | * built. In `WALL_SIDES` order. | |
| 158 | + | * | |
| 159 | + | * A ladder, as it stands: the two flat walls drawn in towards each other until | |
| 160 | + | * the band they leave between them is the room the four strips actually leave, | |
| 161 | + | * and the two upright ones pushed out past the ends of it. Eighteen full-size | |
| 162 | + | * stacks is longer than the middle of a table is deep, so something has to | |
| 163 | + | * overhang; this is a choice about *what* does. | |
| 158 | 164 | */ | |
| 159 | 165 | export const WALL_PLACED: WallNudge[] = [ | |
| 160 | - | { x: 0, y: 0 }, | |
| 161 | - | { x: -1.98, y: -6.7 }, | |
| 162 | - | { x: 0.52, y: -4.91 }, | |
| 163 | - | { x: -1.11, y: 0.46 }, | |
| 166 | + | { x: -0.47, y: 5.06 }, | |
| 167 | + | { x: 1.29, y: -0.76 }, | |
| 168 | + | { x: 0.88, y: -4.31 }, | |
| 169 | + | { x: -1.6, y: 1.13 }, | |
| 164 | 170 | ]; | |
| 165 | 171 | ||
| 166 | 172 | /** How far a wall has been pushed from where it was built, in tiles. */ | |
| ⋯ 106 unchanged lines | |||
modifiedsrc/styles.css+92 −8
| ⋯ 10 unchanged lines | |||
| 11 | 11 | --depth: clamp(226px, 30vh, 307px); | |
| 12 | 12 | --h-len: max(160px, calc(100vw - 2 * var(--depth))); | |
| 13 | 13 | --v-len: 100vh; | |
| 14 | - | /* Height the action buttons occupy, held whether or not any are showing. | |
| 15 | - | The strip is built from its edge inwards, so a bar that grew when 打出 | |
| 16 | - | appeared would shove the melds and discards along with it — the reservation | |
| 17 | - | is what keeps everything above it still. Sized for the tallest button: the | |
| 18 | - | ones that name a tile (打出 五萬, 吃 三萬 四萬) carry a third line. */ | |
| 19 | - | --bar-h: 52px; | |
| 14 | + | /* How tall an action button is: a tile, because that is what they are now. | |
| 15 | + | They sit in the row your hand is in, in the room it already keeps above the | |
| 16 | + | tiles, and they are the same tile as the ones under them — 打出 and 碰 are | |
| 17 | + | things you do with a tile, so they are drawn as one. */ | |
| 18 | + | --bar-h: calc(var(--tile-w) * 1.375); | |
| 20 | 19 | /* Largest tile a strip can hold. Edge inwards it has to fit the melds (a | |
| 21 | 20 | full tile, 1.375 tall, since they are the same tiles as the ones in the | |
| 22 | 21 | hand), the button bar, the hand (1.71 + 6px) and the nameplate (28px), plus | |
| ⋯ 10 unchanged lines | |||
| 33 | 32 | rather than the tiles growing, so a tile is the size it always was and the | |
| 34 | 33 | centre is bigger. | |
| 35 | 34 | ||
| 35 | + | And it used to be 122px rather than 70px: the missing 52 is the action bar, | |
| 36 | + | which had a row of its own and had to be budgeted for whether or not | |
| 37 | + | anything was on it. It has no row now — it hangs at the end of the row the | |
| 38 | + | hand is in, out of the flow — so the strip stopped paying for one. | |
| 39 | + | ||
| 36 | 40 | Tile size otherwise follows the strip's *length*, which is why a window | |
| 37 | 41 | with less height than F11 kept full-size tiles and left the strip's | |
| 38 | 42 | contents to be cut off. */ | |
| 39 | - | --tile-fit: calc((var(--depth) - 122px) / 3.085); | |
| 43 | + | --tile-fit: calc((var(--depth) - 70px) / 3.085); | |
| 40 | 44 | ||
| 41 | 45 | /* One size for every tile on the table — in a hand, in the wall, and lying in | |
| 42 | 46 | the middle. They are the same tiles, so drawing them at different sizes was | |
| ⋯ 404 unchanged lines | |||
| 447 | 451 | height: 100%; | |
| 448 | 452 | display: flex; | |
| 449 | 453 | gap: 8px; | |
| 450 | - | align-items: center; | |
| 454 | + | /* On the same line the tiles stand on, since they are tiles too. */ | |
| 455 | + | align-items: flex-end; | |
| 451 | 456 | justify-content: flex-end; | |
| 452 | 457 | padding-right: calc(var(--tile-w) * 0.4); | |
| 453 | 458 | flex-wrap: wrap; | |
| ⋯ 130 unchanged lines | |||
| 584 | 589 | .btn.discard { | |
| 585 | 590 | background: linear-gradient(180deg, #fff2cf, #e0c070); | |
| 586 | 591 | } | |
| 592 | + | ||
| 593 | + | /* ---------- the action buttons are tiles ---------- */ | |
| 594 | + | /* 打出, 碰, 吃, 槓, 胡, 過 are all things you do *with* a tile, and they sit in | |
| 595 | + | the row your tiles are in — so they are drawn as tiles. Exactly the size of | |
| 596 | + | the ones under them, `--tile-w` by 1.375 of it, ivory face and all, in the | |
| 597 | + | room the hand row already keeps above its tiles for the 摸 tag. The room is | |
| 598 | + | kept whether or not anything is on the bar, which is why the strip does not | |
| 599 | + | move when one arrives. | |
| 600 | + | ||
| 601 | + | Ivory, not coloured: a mahjong tile is ivory and it is the *character* that | |
| 602 | + | is red or green. So the call takes the colour and the face never does. | |
| 603 | + | ||
| 604 | + | Full size only. A phone's bar is a row of its own with room to write on, and | |
| 605 | + | a thumb wants a button it can hit. */ | |
| 606 | + | :root:not(.compact) .actionbar .btn { | |
| 607 | + | flex-direction: column; | |
| 608 | + | width: var(--tile-w); | |
| 609 | + | height: var(--bar-h); | |
| 610 | + | min-height: 0; | |
| 611 | + | padding: 2px 1px; | |
| 612 | + | gap: 0; | |
| 613 | + | border-radius: calc(var(--tile-w) * 0.1); | |
| 614 | + | background: linear-gradient(180deg, var(--ivory), var(--ivory-edge)); | |
| 615 | + | color: var(--ink); | |
| 616 | + | border: none; | |
| 617 | + | box-shadow: 0 2px 3px rgba(0, 0, 0, 0.45), inset 0 calc(var(--tile-w) * -0.06) 0 rgba(0, 0, 0, 0.09); | |
| 618 | + | overflow: hidden; | |
| 619 | + | } | |
| 620 | + | :root:not(.compact) .actionbar .btn:active { | |
| 621 | + | transform: translateY(1px); | |
| 622 | + | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.45); | |
| 623 | + | } | |
| 624 | + | /* The call, the size a tile's own character is drawn at. Two of them fit | |
| 625 | + | across a tile; 過.摸牌 is four, and wraps rather than being cut. */ | |
| 626 | + | :root:not(.compact) .actionbar .btn .bi { | |
| 627 | + | gap: 0; | |
| 628 | + | line-height: 1.02; | |
| 629 | + | } | |
| 630 | + | :root:not(.compact) .actionbar .btn .bi .zh { | |
| 631 | + | font-size: calc(var(--tile-w) * 0.44); | |
| 632 | + | letter-spacing: 0; | |
| 633 | + | word-break: break-all; | |
| 634 | + | } | |
| 635 | + | :root:not(.compact) .actionbar .btn .bi .en { | |
| 636 | + | font-size: calc(var(--tile-w) * 0.185); | |
| 637 | + | letter-spacing: 0.03em; | |
| 638 | + | white-space: normal; | |
| 639 | + | opacity: 0.6; | |
| 640 | + | } | |
| 641 | + | /* Which tile it is about — 打出 五萬, 吃 三萬 四萬. Small, because the tile it | |
| 642 | + | names is lifted out of the row right beside it. */ | |
| 643 | + | :root:not(.compact) .actionbar .btn .bi .bi-sub { | |
| 644 | + | font-size: calc(var(--tile-w) * 0.22); | |
| 645 | + | white-space: normal; | |
| 646 | + | opacity: 0.75; | |
| 647 | + | } | |
| 648 | + | /* The character carries the colour, the way the dragons do. */ | |
| 649 | + | :root:not(.compact) .actionbar .btn.claim.hu { | |
| 650 | + | color: #b3261e; | |
| 651 | + | } | |
| 652 | + | :root:not(.compact) .actionbar .btn.claim.pung, | |
| 653 | + | :root:not(.compact) .actionbar .btn.claim.kong { | |
| 654 | + | color: #1c527d; | |
| 655 | + | } | |
| 656 | + | :root:not(.compact) .actionbar .btn.claim.chow { | |
| 657 | + | color: #1d6640; | |
| 658 | + | } | |
| 659 | + | /* 過 and 摸牌 are not calls, so they are the plain face — and a pass reads as | |
| 660 | + | the quiet one it is. */ | |
| 661 | + | :root:not(.compact) .actionbar .btn.pass, | |
| 662 | + | :root:not(.compact) .actionbar .btn.ghost { | |
| 663 | + | color: rgba(33, 38, 43, 0.65); | |
| 664 | + | } | |
| 665 | + | /* The one that plays a tile: the gold edge every other gold button has, kept | |
| 666 | + | as a rim rather than a face. */ | |
| 667 | + | :root:not(.compact) .actionbar .btn.discard, | |
| 668 | + | :root:not(.compact) .actionbar .btn.proceed { | |
| 669 | + | box-shadow: 0 2px 3px rgba(0, 0, 0, 0.45), inset 0 0 0 2px var(--gold); | |
| 670 | + | } | |
| 587 | 671 | .big-btn { | |
| 588 | 672 | font-size: 22px; | |
| 589 | 673 | padding: 12px 34px; | |
| ⋯ 1862 unchanged lines | |||