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.
122 lines
3.3 KiB
Go
122 lines
3.3 KiB
Go
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)
|
||
}
|
||
}
|
||
}
|