Add tabletop components 486-500 and ten game icons

Fifteen pieces of a games night: Dice, DiceTray, PlayingCard, CardHand,
Deck, Chessboard, GoBoard, MahjongTile, Domino, Roulette, CoinFlip,
BingoCard, TicTacToe, ScoreTrack and Meeple. Component number 500 is
a meeple, which seems right.

Dice tumble, coins turn and wheels spin on a cue, a frame at a time,
and land at once under Reduce Motion. Hands lift the card you point
at and play the one drawn on top; pieces and stones pick the lighter
of Lit and Ink for White, so they read in every palette; the computer
plays a fair O; FREE is a star in every language.

New 12x12 icons: Spade, Club, Diamond, Meeple, King, Queen, Rook,
Bishop, Knight and Pawn.
This commit is contained in:
ZacharyZhang-NY committed 2026-10-12 14:03:58 -04:00
1 parent 7800003418
commit 81003b21ee
32 files changed
+2781 -1

No files matched your search

+121
View File
@@ -0,0 +1,121 @@
package tabletop
import (
"fmt"
"github.com/egoist/mygo/ui"
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
)
// Family is a mahjong suit, or the winds or dragons.
type Family int
const (
Dots Family = iota
Bamboo
Characters
Winds
Dragons
)
var families = [...]struct {
msg string
ranks int
}{{"dots_suit", 9}, {"bamboo", 9}, {"characters", 9}, {"", 4}, {"", 3}}
// Tile: suits rank 1–9, winds 1–4, dragons 1–3.
type Tile struct {
Family Family
Rank int
}
type MahjongTileOptions struct {
// Back shows the tile face down.
Back bool
}
// spots are where 1–9 dots or sticks stand, on 3×3.
var spots = [...][][2]int{{}, {{1, 1}}, {{1, 0}, {1, 2}}, {{0, 0}, {1, 1}, {2, 2}}, {{0, 0}, {2, 0}, {0, 2}, {2, 2}},
{{0, 0}, {2, 0}, {1, 1}, {0, 2}, {2, 2}}, {{0, 0}, {2, 0}, {0, 1}, {2, 1}, {0, 2}, {2, 2}},
{{0, 0}, {2, 0}, {0, 1}, {1, 1}, {2, 1}, {0, 2}, {2, 2}}, {{0, 0}, {1, 0}, {2, 0}, {0, 1}, {2, 1}, {0, 2}, {1, 2}, {2, 2}},
{{0, 0}, {1, 0}, {2, 0}, {0, 1}, {1, 1}, {2, 1}, {0, 2}, {1, 2}, {2, 2}}}
// Tile faces are art: their hanzi stay, whatever the locale.
var (
numerals = []string{"一", "二", "三", "四", "五", "六", "七", "八", "九"}
winds = []string{"east", "south", "west", "north"}
dragons = []string{"red_dragon", "green_dragon", "white_dragon"}
)
const tileW, tileH = 26, 34
// MahjongTile is a suit or honor tile, or its back.
func MahjongTile(c *ui.Context, t Tile, o MahjongTileOptions) *ui.Element {
k := pixel.K(c)
if t.Family < Dots || t.Family > Dragons || t.Rank < 1 || t.Rank > families[t.Family].ranks {
panic(fmt.Sprintf("tabletop: MahjongTile %+v", t))
}
name := k.Msg("tile_back")
switch {
case o.Back:
case t.Family == Winds:
name = k.Msg(winds[t.Rank-1])
case t.Family == Dragons:
name = k.Msg(dragons[t.Rank-1])
default:
name = fmt.Sprintf(k.Msg("tile_name"), t.Rank, k.Msg(families[t.Family].msg))
}
return ui.Box(c).Size(k.D(tileW), k.D(tileH)).Role(ui.RoleImage).Label(name).Draw(func(p *ui.Painter, r ui.Rect) {
g := k.Canvas(p, r)
// The green back shows as a rim below, right.
g.Frame(2, 2, tileW-2, tileH-2, pixel.Flat, pixel.Good)
face := pixel.Lit
if o.Back {
face = pixel.Good
}
g.Frame(0, 0, tileW-2, tileH-2, pixel.Flat, face)
if !o.Back {
tileFace(g, t)
}
})
}
// tileFace paints a tile's face in its 24×32 front.
func tileFace(g pixel.Canvas, t Tile) {
big := func(s string, ink pixel.Slot) { g.Text((24-2*pixel.Measure(s))/2, 2, s, ink, 2) }
switch t.Family {
case Dots:
for i, s := range spots[t.Rank] {
ink := []pixel.Slot{pixel.Info, pixel.Good, pixel.Bad}[i%3]
r := 2
if t.Rank == 1 {
r = 6
}
g.Circle(5+s[0]*7, 7+s[1]*9, r, ink)
g.Dot(5+s[0]*7, 7+s[1]*9, ink)
}
case Bamboo:
for _, s := range spots[t.Rank] {
x, y := 4+s[0]*7, 3+s[1]*9
g.Rect(x, y, 2, 8, pixel.Good)
g.Rect(x, y+3, 2, 1, pixel.Lit)
}
case Characters:
n := numerals[t.Rank-1]
g.Text((24-pixel.Measure(n))/2, 1, n, pixel.Ink, 1)
g.Text((24-pixel.Measure("万"))/2, 15, "万", pixel.Bad, 1)
case Winds:
big([]string{"东", "南", "西", "北"}[t.Rank-1], pixel.Ink)
case Dragons:
switch t.Rank {
case 1:
big("中", pixel.Bad)
case 2:
big("发", pixel.Good)
default:
g.Rect(5, 6, 14, 20, pixel.Info)
g.Rect(7, 8, 10, 16, pixel.Lit)
}
}
}