← Back to Realms

Realm

gno.land/r/g1r6luttvrkksxh9h4asjq2qd8zsd5nlkrzvyjur/pixelgnomes/v2

Overview

Realm Path
gno.land/r/g1r6luttvrkksxh9h4asjq2qd8zsd5nlkrzvyjur/pixelgnomes/v2
Exported Functions
21
State Entries
35
Source Files
4
Total Package Entries
63

Exported Functions

21 exported functions

State

35 state entries

Source Code

FILES
art.gno
go
1package pixelgnomes
2
3import (
4	"strconv"
5	"strings"
6
7	"gno.land/p/nt/ufmt/v0"
8)
9
10// The drawing. A port of the Classic trait sheet: a 20×28 gnome sprite on a
11// 32×32 canvas, with nine traits layered on top. Integer arithmetic only.
12
13const (
14	side       = 32
15	traitCount = 9
16	ink        = "#1b1b22"
17	white      = "#ffffff"
18)
19
20type option struct{ name, color string }
21
22var traitNames = [traitCount]string{"hat", "pattern", "beard", "coat", "skin", "eyes", "accessory", "friend", "background"}
23
24var (
25	hats     = []option{{"red", "#d63c3c"}, {"blue", "#3867d6"}, {"green", "#4f9d4a"}, {"purple", "#8e4fc9"}, {"orange", "#f07f2d"}, {"pink", "#ff7fb5"}, {"teal", "#2aa198"}, {"navy", "#283a8f"}, {"ice", "#6fd3e8"}, {"berry", "#a33b5c"}, {"slate", "#5c6b73"}, {"gold", "#f2c230"}}
26	patterns = []option{{"plain", ""}, {"stripes", ""}, {"dots", ""}, {"star", ""}}
27	beards   = []option{{"snow", "#f4f1ea"}, {"silver", "#cfd6dc"}, {"brown", "#8a5a3b"}, {"ginger", "#d9793b"}, {"black", "#2d2a2a"}, {"blond", "#f2d27a"}, {"lilac", "#d8c4f2"}, {"frost", "#9fd0ff"}}
28	coats    = []option{{"blue", "#3b6fd8"}, {"red", "#c9443a"}, {"moss", "#3f8f4f"}, {"leather", "#7a4e2d"}, {"mustard", "#f2b53a"}, {"denim", "#23406e"}, {"teal", "#2aa198"}, {"mint", "#7ed9c3"}, {"charcoal", "#2c2f36"}, {"violet", "#6a3fa0"}}
29	skins    = []option{{"light", "#f6d3b4"}, {"peach", "#eab28a"}, {"tan", "#c98b5e"}, {"brown", "#95603f"}, {"deep", "#6b422c"}}
30	eyeTypes = []option{{"open", ""}, {"happy", ""}, {"sleepy", ""}, {"wink", ""}}
31	accs     = []option{{"none", ""}, {"rosy cheeks", ""}, {"scarf", ""}, {"glasses", ""}, {"beard flower", ""}}
32	friends  = []option{{"none", ""}, {"mushroom", ""}, {"flower", ""}, {"lantern", ""}, {"star", ""}, {"snail", ""}}
33	bgs      = []option{{"mint", "#bfe8d6"}, {"lemon", "#fbeaa0"}, {"peach", "#ffd6c2"}, {"sky", "#c9e6ff"}, {"mist", "#d9ecf2"}, {"lavender", "#ddd5f5"}, {"sand", "#f3dfb3"}, {"frost", "#cfe6f5"}, {"butter", "#ffe3a3"}, {"blush", "#ffd6e8"}, {"dusk", "#1d2a4a"}, {"midnight", "#141a33"}, {"plum night", "#2a1f4a"}, {"deep night", "#1c2447"}, {"space", "#0f1633"}}
34)
35
36func pools() [traitCount][]option {
37	return [traitCount][]option{hats, patterns, beards, coats, skins, eyeTypes, accs, friends, bgs}
38}
39
40func optionName(trait, idx int) string { return pools()[trait][idx].name }
41
42const digits = "0123456789abcdefghijklmnopqrstuvwxyz"
43
44func traitsOf(id int) [traitCount]int {
45	var t [traitCount]int
46	row := traitTable[(id-1)*traitCount : id*traitCount]
47	p := pools()
48	for i := 0; i < traitCount; i++ {
49		v := strings.IndexByte(digits, row[i])
50		if v < 0 || v >= len(p[i]) {
51			panic("gnomes: corrupt trait table")
52		}
53		t[i] = v
54	}
55	return t
56}
57
58var body = []string{
59	"...........H........",
60	"..........HHh.......",
61	".........HHHh.......",
62	".........HHHHh......",
63	"........HHHHHh......",
64	"........HHHHHHh.....",
65	".......HHHHHHHh.....",
66	"......HHHHHHHHHh....",
67	".....HHHHHHHHHHHh...",
68	"....HHHHHHHHHHHHHh..",
69	"...hhhhhhhhhhhhhhhh.",
70	".....SSSSSSSSSSSS...",
71	".....S..SSSSS..SS...",
72	".....S..SSSSS..SS...",
73	".....RSSSSNNSSSSR...",
74	"....BBSSSSSSSSSSBB..",
75	"....BBBBBBBBBBBBBB..",
76	"...BBBBBBBBBBBBBBBB.",
77	"..CBBBBBBBBBBBBBBBBC",
78	"..CCBBBBBBBBBBBBBBCC",
79	"..SCCCBBBBBBBBBBCCCS",
80	"...CCCCbBBBBBBbCCCC.",
81	"...LLLLLLBBBBLLLLLL.",
82	"...CCCCCCCBBCCCCCCC.",
83	"...PPPPPPPPPPPPPPPP.",
84	"...PPPPPPP..PPPPPPP.",
85	"..OOOOOOOO..OOOOOOOO",
86	"..OOOOOOOO..OOOOOOOO",
87}
88
89// Eyes fill the two 2×2 holes: row, col, paint.
90var eyeCells = map[string][][3]int{
91	"open":   {{12, 6, 'W'}, {12, 7, 'E'}, {13, 6, 'E'}, {13, 7, 'E'}, {12, 13, 'W'}, {12, 14, 'E'}, {13, 13, 'E'}, {13, 14, 'E'}},
92	"happy":  {{12, 6, 'E'}, {12, 7, 'S'}, {13, 6, 'S'}, {13, 7, 'E'}, {12, 13, 'S'}, {12, 14, 'E'}, {13, 13, 'E'}, {13, 14, 'S'}},
93	"sleepy": {{12, 6, 'S'}, {12, 7, 'S'}, {13, 6, 'E'}, {13, 7, 'E'}, {12, 13, 'S'}, {12, 14, 'S'}, {13, 13, 'E'}, {13, 14, 'E'}},
94	"wink":   {{12, 6, 'W'}, {12, 7, 'E'}, {13, 6, 'E'}, {13, 7, 'E'}, {12, 13, 'S'}, {12, 14, 'S'}, {13, 13, 'E'}, {13, 14, 'E'}},
95}
96
97type friendSprite struct {
98	rows []string
99	x, y int
100}
101
102var friendSprites = map[string]friendSprite{
103	"mushroom": {[]string{"..RRR..", ".RWRRR.", "RRRRWRR", "...T...", "..TTT.."}, 0, 25},
104	"star":     {[]string{".Y.", "YYY", ".Y."}, 17, 0},
105	"lantern":  {[]string{".K.", "KYK", "KYK", ".K."}, 26, 22},
106	"flower":   {[]string{".F.", "FYF", ".F.", ".G."}, 6, 19},
107	"snail":    {[]string{"..KKK", ".KYYK", "TTTKK"}, 26, 28},
108}
109
110var friendPaint = map[byte]string{'R': "#e8453c", 'W': white, 'T': "#efe6d2", 'Y': "#ffd84a", 'K': "#5a4636", 'F': "#ff8fb8", 'G': "#4c9a52"}
111
112type canvas struct{ px [side * side]string }
113
114func (c *canvas) put(x, y int, col string) {
115	if x >= 0 && y >= 0 && x < side && y < side {
116		c.px[y*side+x] = col
117	}
118}
119
120const hexDigits = "0123456789abcdef"
121
122// shade scales a #rrggbb colour by pct percent.
123func shade(hex string, pct int) string {
124	v, err := strconv.ParseUint(hex[1:], 16, 32)
125	if err != nil {
126		panic("gnomes: bad colour " + hex)
127	}
128	out := []byte{'#'}
129	for _, shift := range []uint{16, 8, 0} {
130		ch := int((v>>shift)&255) * pct / 100
131		if ch > 255 {
132			ch = 255
133		}
134		out = append(out, hexDigits[ch>>4], hexDigits[ch&15])
135	}
136	return string(out)
137}
138
139// Art draws gnome `tid` as an SVG.
140func Art(tid string) string {
141	id := mustID(tid)
142	t := traitsOf(id)
143	hat, beard, coat, skin, bg := hats[t[0]].color, beards[t[2]].color, coats[t[3]].color, skins[t[4]].color, bgs[t[8]]
144	night := t[8] >= 10
145
146	paint := map[byte]string{
147		'H': hat, 'h': shade(hat, 72), 'S': skin, 'R': shade(skin, 88), 'N': shade(skin, 82),
148		'B': beard, 'b': shade(beard, 82), 'C': coat, 'L': "#3a2a1f", 'P': shade(coat, 55),
149		'O': "#2a2220", 'E': ink, 'W': white,
150	}
151	var c canvas
152	const ox, oy = 6, 3
153	sp := func(x, y int, col string) { c.put(ox+x, oy+y, col) }
154
155	for y, row := range body {
156		for x := 0; x < len(row); x++ {
157			if row[x] != '.' {
158				sp(x, y, paint[row[x]])
159			}
160		}
161	}
162	switch patterns[t[1]].name {
163	case "stripes":
164		for y, row := range body {
165			if y == 3 || y == 6 || y == 9 {
166				for x := 0; x < len(row); x++ {
167					if row[x] == 'H' {
168						sp(x, y, shade(hat, 140))
169					}
170				}
171			}
172		}
173	case "dots":
174		for _, d := range [][2]int{{10, 4}, {9, 7}, {12, 6}, {7, 9}, {11, 9}, {14, 8}} {
175			sp(d[0], d[1], white)
176		}
177	case "star":
178		for _, d := range [][2]int{{10, 5}, {9, 6}, {10, 6}, {11, 6}, {10, 7}} {
179			sp(d[0], d[1], "#ffd84a")
180		}
181	}
182	for _, e := range eyeCells[eyeTypes[t[5]].name] {
183		sp(e[1], e[0], paint[byte(e[2])])
184	}
185	switch accs[t[6]].name {
186	case "rosy cheeks":
187		sp(5, 14, "#ff7f96")
188		sp(6, 14, "#ff9fae")
189		sp(15, 14, "#ff9fae")
190		sp(16, 14, "#ff7f96")
191	case "glasses":
192		const frame = "#3b2f2a"
193		for _, cols := range [][4]int{{5, 6, 7, 8}, {12, 13, 14, 15}} {
194			for _, x := range cols {
195				sp(x, 11, frame)
196				sp(x, 14, frame)
197			}
198			sp(cols[0], 12, frame)
199			sp(cols[0], 13, frame)
200			sp(cols[3], 12, frame)
201			sp(cols[3], 13, frame)
202		}
203		sp(9, 12, frame)
204		sp(10, 12, frame)
205		sp(11, 12, frame)
206	case "scarf":
207		for x := 4; x <= 17; x++ {
208			if x%2 == 1 {
209				sp(x, 15, "#e8b53a")
210			} else {
211				sp(x, 15, "#c7922a")
212			}
213		}
214		for y := 16; y <= 20; y++ {
215			sp(14, y, "#e8b53a")
216			sp(15, y, "#c7922a")
217		}
218	case "beard flower":
219		sp(12, 18, "#ff8fb8")
220		sp(11, 19, "#ff8fb8")
221		sp(13, 19, "#ff8fb8")
222		sp(12, 20, "#ff8fb8")
223		sp(12, 19, "#ffd84a")
224	}
225	if f, ok := friendSprites[friends[t[7]].name]; ok {
226		for y, row := range f.rows {
227			for x := 0; x < len(row); x++ {
228				if col, ok := friendPaint[row[x]]; ok {
229					c.put(f.x+x, f.y+y, col)
230				}
231			}
232		}
233	}
234
235	var b strings.Builder
236	b.WriteString(ufmt.Sprintf(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 %d %d" shape-rendering="crispEdges">`, side, side))
237	b.WriteString(`<rect width="32" height="32" fill="` + bg.color + `"/>`)
238	grass := "#6fbf73"
239	if night {
240		grass = "#2a4a3c"
241		for _, s := range [][2]int{{3, 4}, {9, 2}, {27, 5}, {23, 11}, {2, 14}, {29, 16}, {14, 1}} {
242			b.WriteString(ufmt.Sprintf(`<rect x="%d" y="%d" width="1" height="1" fill="#fff6c9"/>`, s[0], s[1]))
243		}
244	}
245	b.WriteString(`<rect y="29" width="32" height="3" fill="` + grass + `"/>`)
246	b.WriteString(`<rect y="29" width="32" height="1" fill="` + shade(grass, 115) + `"/>`)
247	for y := 0; y < side; y++ {
248		x := 0
249		for x < side {
250			col := c.px[y*side+x]
251			if col == "" {
252				x++
253				continue
254			}
255			n := 1
256			for x+n < side && c.px[y*side+x+n] == col {
257				n++
258			}
259			b.WriteString(ufmt.Sprintf(`<rect x="%d" y="%d" width="%d" height="1" fill="%s"/>`, x, y, n, col))
260			x += n
261		}
262	}
263	b.WriteString(`</svg>`)
264	return b.String()
265}
266

Raw Package Data

Raw JSON data