M9a: stationer i flera våningar med trappor
All checks were successful
release / build-release (push) Successful in 1m34s
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:
@@ -27,6 +27,7 @@ var tileOrder = []level.Tile{
|
||||
level.Wall, // plats 7 = tågvagnen (laddas separat som Train)
|
||||
level.Toilet,
|
||||
level.Wall, // plats 9 = rälsen (laddas separat som Rails)
|
||||
level.StairsUp, level.StairsDown,
|
||||
level.Kiosk,
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ var minimapColors = map[level.Tile]color.RGBA{
|
||||
level.Sign: {90, 140, 230, 255},
|
||||
level.Toilet: {235, 240, 250, 255},
|
||||
level.Kiosk: {228, 59, 68, 255},
|
||||
level.StairsUp: {200, 150, 255, 255},
|
||||
level.StairsDown: {150, 100, 220, 255},
|
||||
}
|
||||
|
||||
// minimapScale väljer pixlar per ruta så att kartan ryms i ungefär en
|
||||
@@ -80,8 +82,16 @@ func (s *LevelScene) drawMinimap(g *Game, screen *ebiten.Image, box image.Rectan
|
||||
}
|
||||
sort.Slice(deps, func(i, j int) bool { return deps[i].track < deps[j].track })
|
||||
|
||||
z := r.Player.Tile.Z
|
||||
floorLabel := ""
|
||||
if l.NumFloors() > 1 {
|
||||
floorLabel = l.FloorName(z)
|
||||
}
|
||||
w := max(mapW, 44) + 6
|
||||
h := mapH + 6 + len(deps)*LineH
|
||||
if floorLabel != "" {
|
||||
h += LineH
|
||||
}
|
||||
px, py := r.Player.WorldPos(TileSize)
|
||||
ox, oy := s.cam.Offset()
|
||||
playerRect := image.Rect(int(px+ox), int(py+oy), int(px+ox)+TileSize, int(py+oy)+TileSize)
|
||||
@@ -89,13 +99,17 @@ func (s *LevelScene) drawMinimap(g *Game, screen *ebiten.Image, box image.Rectan
|
||||
|
||||
vector.DrawFilledRect(screen, float32(rc.Min.X), float32(rc.Min.Y), float32(w), float32(h), colPanel, false)
|
||||
x0, y0 := rc.Min.X+3, rc.Min.Y+3
|
||||
if floorLabel != "" {
|
||||
a.Draw(screen, floorLabel, x0, y0, colKnown)
|
||||
y0 += LineH
|
||||
}
|
||||
for y := 0; y < l.Height(); y++ {
|
||||
for x := 0; x < l.Width(); x++ {
|
||||
p := level.Point{X: x, Y: y}
|
||||
p := level.Point{X: x, Y: y, Z: z}
|
||||
if !r.Fog.Visible(l, p) {
|
||||
continue
|
||||
}
|
||||
t := l.At(x, y)
|
||||
t := l.At(p)
|
||||
c := minimapColors[t]
|
||||
if (t == level.Platform || t == level.WrongPlatform) && r.TrainVisible(t) {
|
||||
c = color.RGBA{255, 255, 255, 255} // tåget är inne
|
||||
@@ -106,6 +120,9 @@ func (s *LevelScene) drawMinimap(g *Game, screen *ebiten.Image, box image.Rectan
|
||||
// Sällskapet: följarna i sina toner, spelaren blinkande vit.
|
||||
for i := len(r.Party.Followers()); i >= 1; i-- {
|
||||
from, _ := r.Party.FollowerTiles(i)
|
||||
if from.Z != z {
|
||||
continue
|
||||
}
|
||||
tint := memberTints[i%len(memberTints)]
|
||||
vector.DrawFilledRect(screen, float32(x0+from.X*sc), float32(y0+from.Y*sc), float32(sc), float32(sc),
|
||||
color.RGBA{uint8(255 * tint[0]), uint8(255 * tint[1]), uint8(255 * tint[2]), 255}, false)
|
||||
|
||||
@@ -125,7 +125,7 @@ func (s *LevelScene) Update(g *Game) error {
|
||||
g.SetScene(NewMapScene(g))
|
||||
return nil
|
||||
}
|
||||
if shopJustPressed() && !s.run.Player.Moving() && s.run.Level.At(s.run.Player.Tile.X, s.run.Player.Tile.Y) == level.Kiosk {
|
||||
if shopJustPressed() && !s.run.Player.Moving() && s.run.Level.At(s.run.Player.Tile) == level.Kiosk {
|
||||
s.openShop(g)
|
||||
return nil
|
||||
}
|
||||
@@ -191,16 +191,18 @@ func (s *LevelScene) Draw(g *Game, screen *ebiten.Image) {
|
||||
y0 := max(0, int(s.cam.Y)/TileSize)
|
||||
x1 := min(lvl.Width(), int(s.cam.X+ScreenW)/TileSize+2)
|
||||
y1 := min(lvl.Height(), int(s.cam.Y+ScreenH)/TileSize+2)
|
||||
z := s.run.Player.Tile.Z // bara spelarens våning ritas
|
||||
var op ebiten.DrawImageOptions
|
||||
for y := y0; y < y1; y++ {
|
||||
for x := x0; x < x1; x++ {
|
||||
if s.rails[level.Point{X: x, Y: y}] {
|
||||
s.drawRails(g, screen, level.Point{X: x, Y: y}, ox, oy)
|
||||
p := level.Point{X: x, Y: y, Z: z}
|
||||
if s.rails[p] {
|
||||
s.drawRails(g, screen, p, ox, oy)
|
||||
continue
|
||||
}
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(ox+float64(x*TileSize), oy+float64(y*TileSize))
|
||||
screen.DrawImage(g.Assets.Tiles[lvl.At(x, y)], &op)
|
||||
screen.DrawImage(g.Assets.Tiles[lvl.At(p)], &op)
|
||||
}
|
||||
}
|
||||
s.drawTrains(g, screen, ox, oy)
|
||||
@@ -211,6 +213,9 @@ func (s *LevelScene) Draw(g *Game, screen *ebiten.Image) {
|
||||
followers := s.run.Party.Followers()
|
||||
for i := len(followers); i >= 1; i-- {
|
||||
from, to := s.run.Party.FollowerTiles(i)
|
||||
if from.Z != z && to.Z != z {
|
||||
continue // på en annan våning
|
||||
}
|
||||
fx := float64(from.X*TileSize) + float64((to.X-from.X)*TileSize)*leader.Progress
|
||||
fy := float64(from.Y*TileSize) + float64((to.Y-from.Y)*TileSize)*leader.Progress
|
||||
facing := sim.Down
|
||||
@@ -296,6 +301,9 @@ func (s *LevelScene) drawHUD(g *Game, screen *ebiten.Image) {
|
||||
dest += " " + r.Level.Destination.Get("ja")
|
||||
}
|
||||
name := r.Level.Name.Get(UILang)
|
||||
if r.Level.NumFloors() > 1 {
|
||||
name += " " + r.Level.FloorName(r.Player.Tile.Z)
|
||||
}
|
||||
a.Draw(screen, name, 4, 0, colText)
|
||||
destX := 4 + a.Width(name) + 12
|
||||
a.Draw(screen, a.T("hud.destination")+": "+dest, destX, 0, colKnown)
|
||||
@@ -411,7 +419,7 @@ func (s *LevelScene) infoLines(a *Assets) (lines []line, accent color.Color, bar
|
||||
lines = append(lines, a.wrapLine(plainLine(fmt.Sprintf("%s: Track %s %s for %s", a.T("hud.english"), tr.Track,
|
||||
tr.Line.Get("en"), tr.Destination.Get("en")), colEnglish), maxW)...)
|
||||
}
|
||||
phase, _ := r.PlatformPhase(lvl.At(tile.X, tile.Y))
|
||||
phase, _ := r.PlatformPhase(lvl.At(tile))
|
||||
switch {
|
||||
case phase == level.Closing:
|
||||
accent = colWarn
|
||||
@@ -461,7 +469,7 @@ func (s *LevelScene) infoLines(a *Assets) (lines []line, accent color.Color, bar
|
||||
return lines, accent, bar
|
||||
}
|
||||
|
||||
switch lvl.At(tile.X, tile.Y) {
|
||||
switch lvl.At(tile) {
|
||||
case level.Toilet:
|
||||
if r.Facility.Active {
|
||||
return []line{plainLine(a.T("hud.toilet_busy"), colDim)}, colGood, true
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user