// Genererar placeholder-grafik tills riktig pixel art finns: // // go run ./tools/genart // // Skriver resources/sprites/tiles.png (en remsa 16×16-rutor i ordningen // vägg, golv, start, perrong, spärr, automat, skylt, tågvagn, toalett, kiosk, räls, // trappa upp, trappa ner) och // resources/sprites/player.png (3 bildrutor × 4 riktningar: ner, upp, // vänster, höger; kolumn 0 = stilla, 1–2 = gå). Ritas med enkla rektanglar // så att allt är deterministiskt och lätt att justera. package main import ( "fmt" "image" "image/color" "image/png" "os" "path/filepath" ) const T = 16 func rgb(r, g, b uint8) color.RGBA { return color.RGBA{r, g, b, 255} } var ( wallDark, wallMid, wallLight = rgb(38, 43, 68), rgb(58, 68, 102), rgb(90, 105, 136) floorA, floorB, grout = rgb(139, 155, 180), rgb(128, 143, 168), rgb(108, 121, 146) yellow, yellowDark = rgb(254, 174, 52), rgb(200, 130, 30) metal, metalDark = rgb(190, 198, 208), rgb(120, 130, 145) screenC, boxDark = rgb(44, 232, 245), rgb(50, 60, 80) signBoard, white = rgb(24, 60, 120), rgb(255, 255, 255) green, greenDark = rgb(99, 199, 77), rgb(60, 140, 50) skin, hair, capC, capDark = rgb(240, 200, 160), rgb(60, 40, 30), rgb(228, 59, 68), rgb(170, 40, 50) jacket, jacketDark = rgb(44, 232, 245), rgb(30, 170, 185) pants, shoe, eye = rgb(38, 43, 68), rgb(255, 255, 255), rgb(38, 43, 68) trainBody, trainStripe = rgb(230, 234, 240), rgb(228, 59, 68) toiletBlue, awningRed = rgb(40, 110, 200), rgb(228, 59, 68) ) type canvas struct { img *image.NRGBA ox, oy int } func (c canvas) rect(x, y, w, h int, col color.RGBA) { for yy := y; yy < y+h; yy++ { for xx := x; xx < x+w; xx++ { c.img.SetNRGBA(c.ox+xx, c.oy+yy, color.NRGBA{col.R, col.G, col.B, col.A}) } } } func floor(c canvas) { c.rect(0, 0, 7, 7, floorA) c.rect(8, 0, 7, 7, floorB) c.rect(0, 8, 7, 7, floorB) c.rect(8, 8, 7, 7, floorA) c.rect(7, 0, 1, 16, grout) c.rect(15, 0, 1, 16, grout) c.rect(0, 7, 16, 1, grout) c.rect(0, 15, 16, 1, grout) } var tiles = []func(canvas){ func(c canvas) { // vägg c.rect(0, 0, 16, 16, wallMid) c.rect(0, 0, 16, 2, wallLight) c.rect(0, 14, 16, 2, wallDark) c.rect(0, 7, 16, 1, wallDark) c.rect(7, 2, 1, 5, wallDark) c.rect(3, 8, 1, 6, wallDark) c.rect(11, 8, 1, 6, wallDark) }, floor, // golv func(c canvas) { // start: golv + dörrmatta floor(c) c.rect(3, 10, 10, 4, green) c.rect(3, 10, 10, 1, greenDark) c.rect(3, 13, 10, 1, greenDark) }, func(c canvas) { // perrong: golv + gul ledlinje + kant floor(c) c.rect(0, 11, 16, 3, yellow) for x := 1; x < 16; x += 4 { c.rect(x, 12, 2, 1, yellowDark) } c.rect(0, 15, 16, 1, wallDark) }, func(c canvas) { // spärr: två stolpar med grön lampa floor(c) for _, x := range []int{1, 11} { c.rect(x, 1, 4, 13, metal) c.rect(x, 1, 4, 1, white) c.rect(x, 13, 4, 1, metalDark) c.rect(x+1, 3, 2, 2, green) } }, func(c canvas) { // automat floor(c) c.rect(3, 1, 10, 13, boxDark) c.rect(4, 2, 8, 5, screenC) c.rect(5, 9, 6, 1, metal) c.rect(5, 11, 2, 1, yellow) c.rect(8, 11, 2, 1, green) }, func(c canvas) { // skylt floor(c) c.rect(7, 7, 2, 9, boxDark) c.rect(2, 1, 12, 7, signBoard) c.rect(2, 1, 12, 1, white) c.rect(3, 3, 10, 1, white) c.rect(3, 5, 6, 1, white) }, func(c canvas) { // tågvagn (ritas på rutan bortom perrongkanten när tåget är inne) c.rect(0, 0, 16, 16, wallDark) c.rect(0, 2, 16, 12, trainBody) c.rect(0, 2, 16, 1, metalDark) c.rect(0, 9, 16, 2, trainStripe) for _, x := range []int{1, 5, 12} { c.rect(x, 4, 3, 4, screenC) } c.rect(9, 4, 2, 9, metalDark) // dörr c.rect(2, 14, 3, 2, boxDark) // hjul c.rect(11, 14, 3, 2, boxDark) }, func(c canvas) { // toalett: vit dörr med blå skylt floor(c) c.rect(3, 1, 10, 13, white) c.rect(3, 1, 10, 1, metalDark) c.rect(5, 3, 6, 5, toiletBlue) c.rect(7, 4, 2, 3, white) // figur c.rect(10, 9, 1, 1, boxDark) }, func(c canvas) { // räls (horisontell; roteras för vertikala spår): slipers och två stålskenor c.rect(0, 0, 16, 16, wallDark) c.rect(0, 0, 16, 1, wallMid) for x := 1; x < 16; x += 4 { c.rect(x, 3, 2, 10, boxDark) // slipers } c.rect(0, 5, 16, 1, metal) c.rect(0, 6, 16, 1, metalDark) c.rect(0, 10, 16, 1, metal) c.rect(0, 11, 16, 1, metalDark) }, func(c canvas) { // trappa upp: steg som blir ljusare uppåt + pil floor(c) for i := 0; i < 5; i++ { shade := uint8(90 + i*25) c.rect(2, 13-i*3, 12, 3, color.RGBA{shade, shade + 10, shade + 30, 255}) } c.rect(7, 1, 2, 4, white) // pil upp c.rect(6, 2, 4, 1, white) c.rect(5, 3, 6, 1, white) }, func(c canvas) { // trappa ner: steg som blir mörkare neråt + pil floor(c) for i := 0; i < 5; i++ { shade := uint8(190 - i*25) c.rect(2, 1+i*3, 12, 3, color.RGBA{shade, shade + 10, shade + 30, 255}) } c.rect(7, 11, 2, 4, boxDark) // pil ner c.rect(6, 13, 4, 1, boxDark) c.rect(5, 12, 6, 1, boxDark) }, func(c canvas) { // kiosk: randig markis och disk floor(c) c.rect(1, 1, 14, 3, awningRed) for x := 1; x < 15; x += 4 { c.rect(x, 1, 2, 3, white) } c.rect(2, 4, 12, 6, boxDark) c.rect(3, 5, 3, 2, screenC) // flaskor c.rect(7, 5, 3, 2, yellow) c.rect(11, 5, 2, 2, green) c.rect(2, 10, 12, 3, metal) // disk }, } // player ritar en figur som tittar åt facing (0 ner, 1 upp, 2 vänster, 3 höger) // i bildruta frame (0 stilla, 1–2 gå). func player(c canvas, facing, frame int) { // ben + skor ly, ry := 11, 11 lx, rx := 5, 9 switch frame { case 1: ly = 10 case 2: ry = 10 } if facing >= 2 { // sidovy: benen närmare varandra, växelvis fram lx, rx = 6, 8 if frame == 1 { lx, rx = 5, 8 } else if frame == 2 { lx, rx = 6, 9 } } c.rect(lx, ly, 2, 14-ly, pants) c.rect(rx, ry, 2, 14-ry, pants) c.rect(lx, 14, 2, 1, shoe) c.rect(rx, 14, 2, 1, shoe) // kropp + armar c.rect(4, 6, 8, 5, jacket) c.rect(4, 6, 8, 1, jacketDark) c.rect(3, 7, 1, 3, jacket) c.rect(12, 7, 1, 3, jacket) // huvud switch facing { case 0: // ner c.rect(4, 1, 8, 5, skin) c.rect(3, 0, 10, 2, capC) c.rect(3, 2, 10, 1, capDark) c.rect(6, 4, 1, 1, eye) c.rect(9, 4, 1, 1, eye) case 1: // upp: bakhuvud c.rect(4, 1, 8, 5, hair) c.rect(3, 0, 10, 2, capC) c.rect(3, 2, 10, 1, capDark) case 2: // vänster c.rect(4, 1, 8, 5, skin) c.rect(9, 1, 3, 5, hair) c.rect(3, 0, 10, 2, capC) c.rect(1, 2, 8, 1, capDark) c.rect(5, 4, 1, 1, eye) case 3: // höger c.rect(4, 1, 8, 5, skin) c.rect(4, 1, 3, 5, hair) c.rect(3, 0, 10, 2, capC) c.rect(7, 2, 8, 1, capDark) c.rect(10, 4, 1, 1, eye) } } func main() { out := filepath.Join(repoRoot(), "resources", "sprites") tilesImg := image.NewNRGBA(image.Rect(0, 0, T*len(tiles), T)) for i, draw := range tiles { draw(canvas{tilesImg, i * T, 0}) } save(filepath.Join(out, "tiles.png"), tilesImg) playerImg := image.NewNRGBA(image.Rect(0, 0, T*3, T*4)) for facing := 0; facing < 4; facing++ { for frame := 0; frame < 3; frame++ { player(canvas{playerImg, frame * T, facing * T}, facing, frame) } } save(filepath.Join(out, "player.png"), playerImg) } func save(path string, img image.Image) { f, err := os.Create(path) if err != nil { fail(err) } defer f.Close() if err := png.Encode(f, img); err != nil { fail(err) } fmt.Println("skrev", path) } func repoRoot() string { dir, _ := os.Getwd() for { if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { return dir } parent := filepath.Dir(dir) if parent == dir { fail(fmt.Errorf("hittade ingen go.mod")) } dir = parent } } func fail(err error) { fmt.Fprintln(os.Stderr, "genart:", err) os.Exit(1) }