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

@@ -47,7 +47,7 @@ func (s *LevelScene) buildTracks() {
} else {
c.Y += k
}
if s.run.Level.At(c.X, c.Y) == level.Wall && (c.X == 0 || c.Y == 0 || c.X == s.run.Level.Width()-1 || c.Y == s.run.Level.Height()-1) {
if s.run.Level.At(c) == level.Wall && (c.X == 0 || c.Y == 0 || c.X == s.run.Level.Width()-1 || c.Y == s.run.Level.Height()-1) {
s.rails[c] = true
}
}
@@ -74,7 +74,7 @@ func (s *LevelScene) drawRails(g *Game, screen *ebiten.Image, c level.Point, ox,
var op ebiten.DrawImageOptions
vertical := false
for _, tr := range s.tracks {
if !tr.horizontal && (c.X == tr.track.X) {
if !tr.horizontal && c.X == tr.track.X && c.Z == tr.track.Z {
vertical = true
}
}
@@ -89,7 +89,11 @@ func (s *LevelScene) drawRails(g *Game, screen *ebiten.Image, c level.Point, ox,
// drawTrains ritar tågen i rätt läge för sin fas.
func (s *LevelScene) drawTrains(g *Game, screen *ebiten.Image, ox, oy float64) {
z := s.run.Player.Tile.Z
for _, tr := range s.tracks {
if tr.platform.Z != z {
continue // tågen på andra våningar syns inte härifrån
}
phase, prog := s.run.PlatformPhase(tr.tile)
// Strålkastare när tåget är på väg in: ett sken från det håll det kommer.
if phase == level.Approaching {
@@ -148,6 +152,10 @@ func (s *LevelScene) updateEffects(g *Game) {
for _, tr := range s.tracks {
phase, prog := r.PlatformPhase(tr.tile)
if last, seen := s.lastPhase[tr.tile]; !seen || last != phase {
if tr.platform.Z != r.Player.Tile.Z {
s.lastPhase[tr.tile] = phase
continue // hörs inte från en annan våning
}
s.lastPhase[tr.tile] = phase
dist := math.Hypot(float64(tr.platform.X*TileSize)-px, float64(tr.platform.Y*TileSize)-py) / TileSize
switch phase {
@@ -159,7 +167,7 @@ func (s *LevelScene) updateEffects(g *Game) {
}
// Vind längs perrongen när tåget rullar in eller ut (och strax före).
windy := phase == level.Arriving || phase == level.Leaving || (phase == level.Approaching && prog > 0.7)
if windy && len(s.particles) < maxParticles {
if windy && tr.platform.Z == r.Player.Tile.Z && len(s.particles) < maxParticles {
dir := 1.0
if phase == level.Arriving {
dir = 1.0 // luften trycks framför tåget: samma håll som tåget rör sig (in från minus-sidan)