M9a: stationer i flera våningar med trappor
All checks were successful
release / build-release (push) Successful in 1m34s

- level: Point har Z; banor kan ha floors (lika stora rutnät med namn),
  gamla grid-banor läses som en våning; trappor U/D på samma x,y en våning
  upp/ner valideras parvis; Neighbors ger grannar över våningar så att
  Path, lösbarhet, spärregel och Unreachable fungerar; skyltar har z
- sim: att kliva på en trappa flyttar spelaren till andra änden
  (FloorChanges); dimman är per våning och avslöjar trappans andra ände;
  tåg hörs/syns bara på egen våning
- game: bara spelarens våning ritas (följare på andra våningar döljs),
  HUD och minikarta visar våningens namn, trappgrafik i genart och färg på
  minikartan
- bana 004 Tokyo (svårighet 3, tre i sällskapet): start under mark på B1,
  trappa upp till 1F med perronger; generatorn gör våningar med trappor
  som flaskhalsar; ordboken utökad
- testbotar och tools/shots förstår trappor; två våningsbilder
- docs/README/wiki uppdaterade

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 11:54:07 +02:00
parent ef6cfaccc8
commit 8fca14e42d
23 changed files with 704 additions and 152 deletions

View File

@@ -3,7 +3,8 @@
// 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) och
// 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.
@@ -142,6 +143,26 @@ var tiles = []func(canvas){
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)

View File

@@ -94,6 +94,14 @@ func buildShots(levelsDir string) error {
if err != nil {
return err
}
l4, err := level.LoadFile(filepath.Join(levelsDir, "004-tokyo.json"))
if err != nil {
return err
}
toStairs, err := moves(l4, l4.SpawnPoint(), l4.Find(level.StairsUp)[0])
if err != nil {
return err
}
// Skyltbilderna tas med några kända ord så att markeringen och den
// delvisa översättningen syns; hjälpen lär sedan in nästa ord.
known := []string{"-know", "改札,番線,方面", "-seed", "7"}
@@ -109,6 +117,7 @@ func buildShots(levelsDir string) error {
{"002-shinjuku", toMachine2 + "; press enter; wait 5; shot $OUT/ticket.png; quit", []string{"-seed", "7", "-know", "渋谷"}},
{"003-shibuya", toMachine3 + "; press enter; pick 池袋; press enter; wait 5; press enter; press right; wait 5; shot $OUT/seats.png; quit",
[]string{"-seed", "7", "-know", "池袋", "-debuffs", "sore_feet"}},
{"004-tokyo", "wait 10; shot $OUT/floors-b1.png; " + toStairs + "; wait 20; shot $OUT/floors-1f.png; quit", []string{"-seed", "7"}},
}
return nil
}
@@ -127,6 +136,9 @@ func moves(l *level.Level, from, to level.Point) (string, error) {
}
}
for i := 1; i < len(path); i++ {
if path[i].Z != path[i-1].Z {
continue // trappan flyttar en till andra våningen i samma steg
}
d := dirName(path[i-1], path[i])
if d != dir {
flush()