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.
119 lines
3.2 KiB
Go
119 lines
3.2 KiB
Go
// Package tabletop is dice, cards, boards and tokens.
|
||
package tabletop
|
||
|
||
import (
|
||
"fmt"
|
||
"strconv"
|
||
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/icon"
|
||
"git.zacharyzhang.com/ZacharyZhang-NY/8bitMyGoUI/pixel"
|
||
)
|
||
|
||
// Suit is a playing card's suit.
|
||
type Suit int
|
||
|
||
const (
|
||
Spades Suit = iota
|
||
Hearts
|
||
Diamonds
|
||
Clubs
|
||
)
|
||
|
||
var suits = [...]struct {
|
||
msg string
|
||
sp *pixel.Sprite
|
||
}{{"spades", icon.Spade}, {"hearts", icon.Heart}, {"diamonds", icon.Diamond}, {"clubs", icon.Club}}
|
||
|
||
// Card's Rank runs from ace, 1, to king, 13.
|
||
type Card struct {
|
||
Rank int
|
||
Suit Suit
|
||
}
|
||
|
||
// check fails on a rank or suit no deck has.
|
||
func (cd Card) check(name string) {
|
||
if cd.Rank < 1 || cd.Rank > 13 || cd.Suit < Spades || cd.Suit > Clubs {
|
||
panic(fmt.Sprintf("tabletop: %s card %+v", name, cd))
|
||
}
|
||
}
|
||
|
||
// ink is a card's color: hearts and diamonds are red.
|
||
func (cd Card) ink() pixel.Slot {
|
||
if cd.Suit == Hearts || cd.Suit == Diamonds {
|
||
return pixel.Bad
|
||
}
|
||
return pixel.Ink
|
||
}
|
||
|
||
// index is the printed rank: A, 2–10, J, Q, K.
|
||
func (cd Card) index() string {
|
||
return [...]string{"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"}[cd.Rank-1]
|
||
}
|
||
|
||
// name reads a card out, as ACE OF SPADES.
|
||
func (cd Card) name(k pixel.Kit) string {
|
||
rank := strconv.Itoa(cd.Rank)
|
||
switch cd.Rank {
|
||
case 1:
|
||
rank = k.Msg("ace")
|
||
case 11, 12, 13:
|
||
rank = k.Msg([...]string{"jack", "queen", "king"}[cd.Rank-11])
|
||
}
|
||
return fmt.Sprintf(k.Msg("card_name"), rank, k.Msg(suits[cd.Suit].msg))
|
||
}
|
||
|
||
const cardW, cardH = 36, 50
|
||
|
||
// drawCard paints a card at x, y: face or back.
|
||
func drawCard(g pixel.Canvas, x, y int, cd Card, back bool) {
|
||
g.Frame(x, y, cardW, cardH, pixel.Flat, pixel.Lit)
|
||
if back {
|
||
g.Rect(x+3, y+3, cardW-6, cardH-6, pixel.Accent)
|
||
for j := 0; j < cardH-10; j += 4 {
|
||
for i := j / 4 % 2 * 2; i < cardW-10; i += 4 {
|
||
g.Rect(x+5+i, y+5+j, 2, 2, pixel.AccentLit)
|
||
}
|
||
}
|
||
return
|
||
}
|
||
ink, idx := cd.ink(), cd.index()
|
||
g.Micro(x+3, y+3, idx, ink, 1)
|
||
g.Micro(x+cardW-3-pixel.MicroWidth(idx), y+cardH-8, idx, ink, 1)
|
||
// Every card shows its suit in a corner.
|
||
g.Sprite(x+cardW-14, y+2, suits[cd.Suit].sp, 0, ink, 1)
|
||
// Court cards show their piece, the rest a big pip.
|
||
sp := suits[cd.Suit].sp
|
||
if cd.Rank > 10 {
|
||
sp = [...]*pixel.Sprite{icon.Knight, icon.Queen, icon.King}[cd.Rank-11]
|
||
}
|
||
g.Sprite(x+(cardW-24)/2, y+(cardH-24)/2+2, sp, 0, ink, 2)
|
||
}
|
||
|
||
// pips places 0–6 pips on a face of side s.
|
||
func pips(g pixel.Canvas, x, y, s, n int, ink pixel.Slot) {
|
||
if n < 0 || n > 6 {
|
||
panic(fmt.Sprintf("tabletop: %d pips", n))
|
||
}
|
||
spots := [...][][2]int{{}, {{1, 1}}, {{0, 0}, {2, 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}}}[n]
|
||
d := max(2, s/7)
|
||
for _, p := range spots {
|
||
g.Rect(x+s*(p[0]+1)/4-d/2, y+s*(p[1]+1)/4-d/2, d, d, ink)
|
||
}
|
||
}
|
||
|
||
// shades are Lit and Ink, lighter first: White looks white.
|
||
func shades(k pixel.Kit) (light, dark pixel.Slot) {
|
||
lum := func(s pixel.Slot) int {
|
||
c := k.C(s)
|
||
return 299*int(c.R) + 587*int(c.G) + 114*int(c.B)
|
||
}
|
||
if lum(pixel.Lit) >= lum(pixel.Ink) {
|
||
return pixel.Lit, pixel.Ink
|
||
}
|
||
return pixel.Ink, pixel.Lit
|
||
}
|
||
|
||
// players color tokens, one slot a player.
|
||
var players = []pixel.Slot{pixel.Bad, pixel.Info, pixel.Good, pixel.Warn, pixel.Magic, pixel.Ink}
|