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}
266Raw Package Data
Raw JSON data