M2: tåg med tidtabell, spärrar med kort, skyltar, vinst/förlust
All checks were successful
release / build-release (push) Successful in 1m35s
All checks were successful
release / build-release (push) Successful in 1m35s
- level: Schedule (ankomst/intervall/uppehåll) med Status(t), spårnummer, avgift, skylttexter per i-ruta, Path (BFS) och UngatedPlatforms – regeln att perronger bara nås via spärrar testas för alla banor - sim.Run: klocka, tågkort (saldo dras vid spärr, tomt kort stoppas), ombordstigning med Enter när tåget är inne, vinst/förlust; headless bot-test klarar bana 001 och förlorar på fel tåg - game: perronginfo (avgår om / nästa tåg om), skyltruta, nekad spärr, tågvagn som ritas när tåget är inne, inforutan flyttas upp när spelaren står nära nederkanten; resultat med spärrpassager och saldo - bana 001 ombyggd så spärrarna är flaskhalsar; bana 002 omgenererad med spärrar runt perrongerna och skylttexter - tools/shots räknar ut vägar med BFS; skriptkommandot waittrain - docs/README/wiki uppdaterade med nya bilder Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
// 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) och
|
||||
// vägg, golv, start, perrong, spärr, automat, skylt, tågvagn) 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.
|
||||
@@ -33,6 +33,7 @@ var (
|
||||
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)
|
||||
)
|
||||
|
||||
type canvas struct {
|
||||
@@ -109,6 +110,18 @@ var tiles = []func(canvas){
|
||||
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)
|
||||
},
|
||||
}
|
||||
|
||||
// player ritar en figur som tittar åt facing (0 ner, 1 upp, 2 vänster, 3 höger)
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
// go run ./tools/shots
|
||||
//
|
||||
// Bygger debug-binären, kör den med -script för varje bild och skriver
|
||||
// PNG:er till doc/img/. Kör om detta när grafik eller UI ändrats, och
|
||||
// committa bilderna – README och wikin pekar på dem.
|
||||
// PNG:er till doc/img/. Vägarna genom banorna räknas ut med BFS från
|
||||
// bandatan. Kör om detta när grafik, UI eller banor ändrats, och committa
|
||||
// bilderna – README och wikin pekar på dem.
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -13,26 +14,88 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level"
|
||||
)
|
||||
|
||||
type shot struct {
|
||||
file string
|
||||
level string
|
||||
script string
|
||||
script string // $OUT ersätts med doc/img-mappen
|
||||
}
|
||||
|
||||
var shots = []shot{
|
||||
{"menu.png", "", "wait 10; shot %s; quit"},
|
||||
{"level-001.png", "001-flygplatsen", "move right 4; move down 2; wait 5; shot %s; quit"},
|
||||
{"level-002.png", "002-shinjuku", "move down 6; move right 4; wait 5; shot %s; quit"},
|
||||
{"result.png", "001-flygplatsen",
|
||||
"move down 2; move right 2; move down 2; move left 2; move down 4; move right 17; move down 1; move left 1; wait 15; shot %s; quit"},
|
||||
// shots byggs i main eftersom vägarna räknas ut från bandatan (BFS), så att
|
||||
// bilderna överlever ändringar i banorna.
|
||||
var shots []shot
|
||||
|
||||
func buildShots(levelsDir string) error {
|
||||
l, err := level.LoadFile(filepath.Join(levelsDir, "001-flygplatsen.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sign := l.Find(level.Sign)[1] // skylten vid spärrarna
|
||||
goal := l.Find(level.Platform)[0]
|
||||
toSign, err := moves(l, l.SpawnPoint(), sign)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
toGoal, err := moves(l, sign, goal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
shots = []shot{
|
||||
{"", "wait 10; shot $OUT/menu.png; quit"},
|
||||
{"001-flygplatsen", "move right 4; move down 2; wait 5; shot $OUT/level-001.png; quit"},
|
||||
{"002-shinjuku", "move down 6; move right 4; wait 5; shot $OUT/level-002.png; quit"},
|
||||
{"001-flygplatsen", toSign + "; wait 5; shot $OUT/sign.png; " + toGoal +
|
||||
"; waittrain; wait 30; shot $OUT/platform.png; press enter; wait 5; shot $OUT/result.png; quit"},
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// moves gör om kortaste vägen mellan två rutor till "move DIR N"-steg.
|
||||
func moves(l *level.Level, from, to level.Point) (string, error) {
|
||||
path := l.Path(from, to, l.CanEnter)
|
||||
if path == nil {
|
||||
return "", fmt.Errorf("ingen väg %v -> %v i %s", from, to, l.ID)
|
||||
}
|
||||
var parts []string
|
||||
dir, n := "", 0
|
||||
flush := func() {
|
||||
if n > 0 {
|
||||
parts = append(parts, fmt.Sprintf("move %s %d", dir, n))
|
||||
}
|
||||
}
|
||||
for i := 1; i < len(path); i++ {
|
||||
d := dirName(path[i-1], path[i])
|
||||
if d != dir {
|
||||
flush()
|
||||
dir, n = d, 0
|
||||
}
|
||||
n++
|
||||
}
|
||||
flush()
|
||||
return strings.Join(parts, "; "), nil
|
||||
}
|
||||
|
||||
func dirName(a, b level.Point) string {
|
||||
switch {
|
||||
case b.X > a.X:
|
||||
return "right"
|
||||
case b.X < a.X:
|
||||
return "left"
|
||||
case b.Y > a.Y:
|
||||
return "down"
|
||||
default:
|
||||
return "up"
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
root := repoRoot()
|
||||
out := filepath.Join(root, "doc", "img")
|
||||
must(os.MkdirAll(out, 0o755))
|
||||
must(buildShots(filepath.Join(root, "resources", "data", "levels")))
|
||||
|
||||
build := exec.Command("go", "run", "./tools/build", "-mode", "debug")
|
||||
build.Dir, build.Stdout, build.Stderr = root, os.Stdout, os.Stderr
|
||||
@@ -43,14 +106,14 @@ func main() {
|
||||
exe += ".exe"
|
||||
}
|
||||
for _, s := range shots {
|
||||
args := []string{"-script", fmt.Sprintf(s.script, filepath.Join(out, s.file))}
|
||||
args := []string{"-script", strings.ReplaceAll(s.script, "$OUT", out)}
|
||||
if s.level != "" {
|
||||
args = append(args, "-level", s.level)
|
||||
}
|
||||
run := exec.Command(exe, args...)
|
||||
run.Dir, run.Stdout, run.Stderr = filepath.Dir(exe), os.Stdout, os.Stderr
|
||||
if err := run.Run(); err != nil {
|
||||
fail(fmt.Errorf("%s: %w", s.file, err))
|
||||
fail(fmt.Errorf("%s: %w", s.script, err))
|
||||
}
|
||||
}
|
||||
fmt.Println("klart:", out)
|
||||
|
||||
Reference in New Issue
Block a user