// Pixel art: an 8x8 icon set and small primitives shared by the LCD scene // and the DOM (buttons, diary stickers, gift shelf). export const C = { lcd: "#cfdcc9", lcdDark: "#2b3833", lcdNight: "#26302d", lcdShade: "#b9cbb5", ink: "#1f2a27", mid: "#7f9a91", white: "#f6f2e9", pink: "#ec9fae", red: "#d9636f", green: "#79ad74", dgreen: "#4f8352", yellow: "#efc75e", orange: "#e8894d", brown: "#a3744f", blue: "#7ea8cf", lav: "#b7a3d9", }; const KEY: Record = { "#": C.ink, o: C.mid, "+": C.white, p: C.pink, r: C.red, g: C.green, G: C.dgreen, y: C.yellow, O: C.orange, b: C.brown, u: C.blue, l: C.lav, }; const S = (rows: string) => rows.trim().split(/\s+/); export const SPRITES: Record = { strawberry: S(`...gg... ..gGgg.. .rrrrrr. rr+rrrrr rrrrr+rr .rr+rrr. ..rrrr.. ...rr...`), cookie: S(`..bbbb.. .bbbbbb. bb#bbbbb bbbbb#bb bb#bbbbb bbbb#bbb .bbbbbb. ..bbbb..`), "rice ball": S(`...oo... ..o++o.. .o++++o. .o++++o. o++++++o o+####+o o+####+o .o####o.`), carrot: S(`......g. ....g.g. ....OOg. ...OOO.. ..OOO... .OOO.... OOO..... O.......`), pudding: S(`...bb... ..bbbb.. ..yyyy.. .yyyyyy. .yyyyyy. yyyyyyyy oooooooo .oooooo.`), apple: S(`....b... ...bg... .rr.rr.. rrrrrrr. rr+rrrr. rrrrrrr. .rrrrr.. ..r.r...`), flower: S(`.pp..pp. pppppppp .ppyypp. pppyyppp .pppppp. pp.gg.pp ...gg... ..gggg..`), pebble: S(`........ ........ ...ooo.. ..oo+oo. .ooooooo .oooooo# ..####.. ........`), star: S(`...yy... ...yy... yyyyyyyy .yyyyyy. ..yyyy.. .yyyyyy. .yy..yy. y......y`), leaf: S(`......gg ....gggg ...ggggg ..ggg+gg .gg+ggg. .g+ggg.. .+gg.... +.......`), shell: S(`...pp... ..p+pp.. .pp+p+p. .p+pp+p. p+pp+pp+ pppppppp .pp..pp. ........`), heart: S(`........ .pp..pp. pppppppp pppppppp .pppppp. ..pppp.. ...pp... ........`), drawing: S(`+++++++. +uu++++# +u+u+p+# ++++ppp# +g+++p+# +gg++++# +++++++# .#######`), feather: S(`......u. .....uu. ....uuu. ...uu+.. ..uu+... .uu+.... .u+..... +.......`), cloud: S(`........ ...oo... ..o++o.. .o+++oo. o++++++o o++++++o .oooooo. ........`), moon: S(`...yyy.. ..yy.... .yy..... .yy..... .yy..... ..yy.... ...yyy.. ........`), sun: S(`y..y..y. .y.y.y.. ..yyy... yyyyyyy. ..yyy... .y.y.y.. y..y..y. ........`), frog: S(`.gg..gg. g+#gg+#g gggggggg gpggggpg gg####gg .gggggg. gg....gg ........`), rain: S(`...oo... ..o++o.. .o++++o. .oooooo. .u..u... ..u..u.. .u..u... ........`), music: S(`...###.. ...#.##. ...#..#. ...#.... .###.... ####.... .##..... ........`), cat: S(`o.....o. oo...oo. ooooooo. o#ooo#o. ooopooo. .ooooo.. ........ ........`), ghost: S(`..oooo.. .o++++o. o+#++#+o o++++++o o++pp++o o++++++o o+o++o+o .o.oo.o.`), sparkle: S(`...y.... ...y.... ..yyy... yyy+yyy. ..yyy... ...y.... ...y.... ........`), ball: S(`..####.. .#++++#. #+pp+++# #+pp+++# #++++uu# #+++uuu# .#++uu#. ..####..`), bubbles: S(`.oo..... o++o.oo. o++oo++o .oo.o++o .....oo. ..oo.... .o++o... ..oo....`), bulb: S(`..####.. .#yyyy#. #yy++yy# #yy+yyy# .#yyyy#. ..#oo#.. ..#oo#.. ...##...`), pill: S(`........ ...##... ..#pp#.. ..#pp#.. ..#++#.. ..#++#.. ...##... ........`), book: S(`.######. #ppppp+# #ppppp+# #pp#pp+# #ppppp+# #ppppp+# .######. ........`), poop: S(`....b... ...bb... ..bbbb.. ..b#b#.. .bbbbbb. bbbbbbbb ........ ........`), note: S(`..###... ..#.#... ..#..... ..#..... ###..... ###..... ........ ........`), zz: S(`####.... ..#..... .#...... ####.... ........ ........ ........ ........`), bang: S(`.##..... .##..... .##..... .##..... ........ .##..... ........ ........`), tear: S(`u. uu .u`), smallheart: S(`p.p ppp .p.`), crumb: S(`b. .b`), }; export const SPRITE_W = (name: string) => SPRITES[name][0].length; export function drawSprite( ctx: CanvasRenderingContext2D, name: string, x: number, y: number, { color = null, flip = false }: { color?: string | null; flip?: boolean } = {}, ) { const rows = SPRITES[name]; if (!rows) return; for (let j = 0; j < rows.length; j++) { const row = rows[j]; for (let i = 0; i < row.length; i++) { const ch = row[flip ? row.length - 1 - i : i]; if (ch === ".") continue; ctx.fillStyle = color || KEY[ch] || C.ink; ctx.fillRect(Math.round(x) + i, Math.round(y) + j, 1, 1); } } } const urlCache = new Map(); export function spriteURL(name: string, scale = 4) { const key = `${name}@${scale}`; if (urlCache.has(key)) return urlCache.get(key)!; const rows = SPRITES[name]; const c = document.createElement("canvas"); c.width = rows[0].length * scale; c.height = rows.length * scale; const ctx = c.getContext("2d")!; ctx.scale(scale, scale); drawSprite(ctx, name, 0, 0); const url = c.toDataURL(); urlCache.set(key, url); return url; } // Tiny 3x5 pixel font for the LCD clock and score. const FONT: Record = { 0: "###,#.#,#.#,#.#,###", 1: ".#.,##.,.#.,.#.,###", 2: "###,..#,###,#..,###", 3: "###,..#,.##,..#,###", 4: "#.#,#.#,###,..#,..#", 5: "###,#..,###,..#,###", 6: "###,#..,###,#.#,###", 7: "###,..#,.#.,.#.,.#.", 8: "###,#.#,###,#.#,###", 9: "###,#.#,###,..#,###", ":": "...,.#.,...,.#.,...", x: "...,#.#,.#.,#.#,...", "?": "###,..#,.##,...,.#.", "!": ".#.,.#.,.#.,...,.#.", " ": "...,...,...,...,...", }; export function drawText( ctx: CanvasRenderingContext2D, text: string | number, x: number, y: number, color = C.ink, ) { ctx.fillStyle = color; for (const ch of String(text)) { const g = FONT[ch]; if (g) g.split(",").forEach((row, j) => [...row].forEach((p, i) => { if (p === "#") ctx.fillRect(x + i, y + j, 1, 1); }), ); x += 4; } }