M7b: tågfaser, räls, in-/utkörning, vind, ankomstindikator och melodier
All checks were successful
release / build-release (push) Successful in 1m42s
All checks were successful
release / build-release (push) Successful in 1m42s
- level.Schedule.Phase: away/approaching/arriving/dwelling/closing/leaving ur tidtabellen med progress; Boardable (öppna dörrar) och Present; validering av minsta uppehåll/borta-tid; TrackTile för rälsen - sim.Run: ombordstigning bara med öppna dörrar, TrainVisible via Present, PlatformPhase och NearTrack (närmaste perrong inom två rutor) - game/trains.go: rälsrutor längs kantraden (roterade för vertikala spår), tre vagnar som bromsar in och accelererar ut längs rälsen, strålkastare under inrullningen, blinkande lampor vid kanten för den som står nära, dammpartiklar längs perrongen; kameran får en rutas marginal så att kanträlsen syns - game/audio.go: ebiten/v2/audio med avgångsmelodi (approaching) och dörrsignal (closing), volym efter avstånd, -mute; tools/gensound syntetiserar placeholder-WAV:ar - HUD: fasberoende perrongtext och "tåg på väg in" nära rälsen; skriptkommandot waitphase; skärmdump av ankomsten - plan (Björn 2026-10-24): räls/väg-grafik och melodierna i M7b; folk/NPC:er, vänster-/högertrafik, bredare gångar, reklam- och ljudröra som nytt steg M9b – i spelbeskrivning, roadmap, testplan - docs/README/wiki uppdaterade Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
"math/rand/v2"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -34,6 +35,13 @@ type LevelScene struct {
|
||||
cam *sim.Camera
|
||||
animT float64
|
||||
hideMap bool // M döljer/visar minikartan
|
||||
|
||||
// Tåg och effekter (M7b).
|
||||
tracks []trackInfo
|
||||
rails map[level.Point]bool
|
||||
lastPhase map[level.Tile]level.Phase
|
||||
particles []particle
|
||||
rng *rand.Rand // bara för partiklar (ingen spellogik)
|
||||
}
|
||||
|
||||
// NewLevelScene laddar banan och ställer spelaren på startrutan. Kunskap,
|
||||
@@ -48,10 +56,27 @@ func NewLevelScene(a *Assets, id string, g *Game, seed rng.Seed) (*LevelScene, e
|
||||
run: sim.NewRun(lvl, sim.RunOptions{Dict: a.Dict, Know: g.Know, Wallet: g.Wallet, Debuffs: g.Profile.Debuffs, Seed: seed}),
|
||||
cam: sim.NewCamera(ScreenW, ScreenH, float64(lvl.Width()*TileSize), float64(lvl.Height()*TileSize)),
|
||||
}
|
||||
s.cam.Margin = TileSize // rälsen ligger i kantraden: visa en ruta utanför kartan
|
||||
s.lastPhase = map[level.Tile]level.Phase{}
|
||||
s.rng = rand.New(rand.NewPCG(1, 2))
|
||||
s.buildTracks()
|
||||
for _, tr := range s.tracks { // ingen melodi för fasen banan råkar börja i
|
||||
s.lastPhase[tr.tile], _ = s.run.PlatformPhase(tr.tile)
|
||||
}
|
||||
s.followPlayer()
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// TrackPhaseName är fasen för närmaste perrong inom indikatoravståndet
|
||||
// (skriptläget), "" om ingen är nära.
|
||||
func (s *LevelScene) TrackPhaseName() string {
|
||||
_, _, phase, _, _, ok := s.run.NearTrack(indicatorDist)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return phase.String()
|
||||
}
|
||||
|
||||
// PlayerTile returnerar rutan spelaren står på/lämnar (används av skriptläget).
|
||||
func (s *LevelScene) PlayerTile() level.Point { return s.run.Player.Tile }
|
||||
|
||||
@@ -77,6 +102,7 @@ func (s *LevelScene) followPlayer() {
|
||||
}
|
||||
|
||||
func (s *LevelScene) Update(g *Game) error {
|
||||
s.updateEffects(g)
|
||||
if s.run.Mini != nil {
|
||||
// Minispel: all input går dit; Esc avbryter minispelet, inte banan.
|
||||
mx, my := miniMoveXY()
|
||||
@@ -146,18 +172,6 @@ func (s *LevelScene) MiniPick(text string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// trainTile är rutan "bortom perrongkanten" där tågvagnen ritas: första
|
||||
// väggruta nedanför, till höger, till vänster eller ovanför perrongen.
|
||||
func (s *LevelScene) trainTile(p level.Point) level.Point {
|
||||
for _, d := range [...]level.Point{{X: 0, Y: 1}, {X: 1, Y: 0}, {X: -1, Y: 0}, {X: 0, Y: -1}} {
|
||||
n := level.Point{X: p.X + d.X, Y: p.Y + d.Y}
|
||||
if s.run.Level.At(n.X, n.Y) == level.Wall {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (s *LevelScene) Draw(g *Game, screen *ebiten.Image) {
|
||||
screen.Fill(color.RGBA{24, 20, 37, 255})
|
||||
lvl := s.run.Level
|
||||
@@ -172,23 +186,17 @@ func (s *LevelScene) Draw(g *Game, screen *ebiten.Image) {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
// Tåg som står inne.
|
||||
for _, t := range []level.Tile{level.Platform, level.WrongPlatform} {
|
||||
if !s.run.TrainVisible(t) {
|
||||
continue
|
||||
}
|
||||
for _, p := range lvl.Find(t) {
|
||||
tt := s.trainTile(p)
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(ox+float64(tt.X*TileSize), oy+float64(tt.Y*TileSize))
|
||||
screen.DrawImage(g.Assets.Train, &op)
|
||||
}
|
||||
}
|
||||
s.drawTrains(g, screen, ox, oy)
|
||||
s.drawParticles(screen, ox, oy)
|
||||
|
||||
// Följarna ritas bakifrån så att ledaren hamnar överst.
|
||||
leader := s.run.Player
|
||||
@@ -221,6 +229,7 @@ func (s *LevelScene) Draw(g *Game, screen *ebiten.Image) {
|
||||
op.GeoM.Translate(ox+math.Round(px), oy+math.Round(py))
|
||||
screen.DrawImage(g.Assets.PlayerFrame(leader.Facing, leader.Moving(), s.animT), &op)
|
||||
s.drawUrge(g, screen, s.run.Party.Members[0], int(ox+math.Round(px)), int(oy+math.Round(py)))
|
||||
s.drawIndicator(screen, ox, oy)
|
||||
|
||||
s.drawHUD(g, screen)
|
||||
if s.run.Mini != nil {
|
||||
@@ -387,15 +396,32 @@ 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)...)
|
||||
}
|
||||
if at {
|
||||
phase, _ := r.PlatformPhase(lvl.At(tile.X, tile.Y))
|
||||
switch {
|
||||
case phase == level.Closing:
|
||||
accent = colWarn
|
||||
lines = append(lines, plainLine(fmt.Sprintf("%s %.0f s", a.T("hud.closing"), math.Ceil(secs)), colWarn))
|
||||
case at:
|
||||
accent = colGood
|
||||
lines = append(lines, plainLine(fmt.Sprintf("%s %.0f s %s", a.T("hud.departs_in"), math.Ceil(secs), a.T("hud.board")), colGood))
|
||||
} else {
|
||||
case phase == level.Arriving:
|
||||
accent = colWarn
|
||||
lines = append(lines, plainLine(a.T("hud.arriving"), colWarn))
|
||||
case phase == level.Approaching:
|
||||
accent = colWarn
|
||||
lines = append(lines, plainLine(fmt.Sprintf(a.T("hud.approaching"), math.Ceil(secs)), colWarn))
|
||||
case phase == level.Leaving:
|
||||
lines = append(lines, plainLine(fmt.Sprintf("%s %s %.0f s", a.T("hud.leaving"), a.T("hud.next_train_in"), math.Ceil(secs)), colDim))
|
||||
default:
|
||||
accent = colWarn
|
||||
lines = append(lines, plainLine(fmt.Sprintf("%s %.0f s", a.T("hud.next_train_in"), math.Ceil(secs)), colWarn))
|
||||
}
|
||||
return lines, accent, false
|
||||
}
|
||||
// Nära rälsen (men inte på perrongen): man ser och hör tåget komma.
|
||||
if _, _, phase, _, dist, ok := r.NearTrack(indicatorDist); ok && dist > 0 && phase == level.Approaching && r.SignHere() == nil {
|
||||
return []line{plainLine(a.T("hud.train_near"), colWarn)}, colWarn, false
|
||||
}
|
||||
|
||||
if sg := r.SignHere(); sg != nil {
|
||||
know := r.Know
|
||||
|
||||
Reference in New Issue
Block a user