Files
claude f762f9554e
All checks were successful
release / build-release (push) Successful in 1m34s
M9: navigeringslägen, linjenät och terminal
internal/nav med resources/data/network.json (76 stationer, 11 linjer,
Yamanote som ringlinje). Tåg bedöms från lineId + terminus mot banans
station/to: rätt, fel håll, för långt bort (fler än 12 stationer) eller
ingen trafik; correct räknas ut för nätkopplade banor, 1-4 rätta tåg.
Tågresan visar linjens riktiga stationer. Nya förlustorsaker "Fel håll"
och "För långt bort". Listläge (nästa tåg nere till vänster) och
skyltläge (+50 % poäng) växlas med N på bankartan och sparas; -mode.
Terminalen: biljettautomaten visar linjetavlan på nätbanor. Perronger
P p Q q R r. Bana 006 Ikebukuro med fyra perronger. Banorna 001-005
kopplade till nätet. Skript: waitover och pick calm; poängraden
radbryts; skärmdumpar uppdaterade.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 13:07:36 +02:00

120 lines
3.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package minigame
import "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/rng"
// Ride är tågresan: stationerna passerar en i taget med "次は …"-utrop;
// tryck Enter vid ditt resmål. Fel station eller att åka förbi är misslyckat.
type Ride struct {
base
Stops []string // stationsnamn på japanska i ordning
Dest int // index för resmålet, -1 om det inte finns på linjen (fel tåg)
Index int // aktuell station
Timer float64 // sekunder kvar vid aktuell station
StopSec float64
GotOff int // index där spelaren klev av, -1 = inte än
// Trängsel: vid stationer där man inte ska av vill andra passagerare ut.
Crowd int // vagnens trängselnivå 0–2
WantsOut []bool // per station: någon vill förbi
LetPass bool // spelaren har släppt förbi vid aktuell station
Blocked int // stationer där ingen släpptes förbi (avdrag)
}
// Tid per station efter trängsel: 2,5 / 2,0 / 1,5 s.
const rideStopSec = 2.5
// RideStopSec är tiden per station för en trängselnivå.
func RideStopSec(crowd int) float64 { return rideStopSec - 0.5*float64(max(0, min(2, crowd))) }
// NewRide bygger linjen: fillers är andra stationsnamn (blandas), dest läggs
// på en seedad plats (aldrig först) om det är rätt tåg, annars utelämnas det.
// crowd (0–2) ger kortare tid per station och passagerare som vill förbi.
func NewRide(seed rng.Seed, dest string, fillers []string, stops int, correctTrain bool, crowd int) *Ride {
r := newRand(seed, "ride")
f := append([]string(nil), fillers...)
r.shuffle(len(f), func(i, j int) { f[i], f[j] = f[j], f[i] })
stops = max(2, stops)
di := -1
if correctTrain {
di = 1 + r.intn(stops-1)
}
var names []string
for i := 0; i < stops; i++ {
switch {
case i == di:
names = append(names, dest)
case len(f) > 0:
names = append(names, f[0])
f = f[1:]
default:
names = append(names, UnknownStop) // slut på namn: en namnlös station
}
}
return newRide(r, names, di, crowd)
}
// NewRideStops bygger resan av givna stationer i ordning (linjenätets
// riktiga stationer); dest är resmålets index, -1 om det inte kommer.
func NewRideStops(seed rng.Seed, stops []string, dest int, crowd int) *Ride {
r := newRand(seed, "ride")
if len(stops) < 2 {
stops = append(append([]string(nil), stops...), UnknownStop, UnknownStop)[:2]
}
if dest >= len(stops) {
dest = -1
}
return newRide(r, append([]string(nil), stops...), dest, crowd)
}
func newRide(r *randSrc, stops []string, dest int, crowd int) *Ride {
crowd = max(0, min(2, crowd))
sec := RideStopSec(crowd)
g := &Ride{StopSec: sec, Timer: sec, GotOff: -1, Dest: dest, Crowd: crowd, Stops: stops}
g.WantsOut = make([]bool, len(g.Stops))
chance := [...]float64{0, 0.4, 0.7}[crowd]
for i := range g.Stops {
if i != g.Dest && r.float() < chance {
g.WantsOut[i] = true
}
}
return g
}
// NeedsLetPass rapporterar om någon vill förbi vid aktuell station just nu.
func (g *Ride) NeedsLetPass() bool { return g.WantsOut[g.Index] && !g.LetPass }
// UnknownStop används när det inte finns fler stationsnamn att ta av.
const UnknownStop = "・・・"
// Current är stationen tåget står vid.
func (g *Ride) Current() string { return g.Stops[g.Index] }
// Update låter stationerna passera och tar emot "kliv av".
func (g *Ride) Update(in Input, dt float64) {
if g.done {
return
}
if in.Confirm {
g.GotOff = g.Index
g.done = true
g.success = g.Index == g.Dest
return
}
if in.MoveX < 0 && g.NeedsLetPass() {
g.LetPass = true // kliv åt sidan
}
g.Timer -= dt
if g.Timer <= 0 {
if g.NeedsLetPass() {
g.Blocked++
}
g.Index++
g.Timer = g.StopSec
g.LetPass = false
if g.Index >= len(g.Stops) {
g.Index = len(g.Stops) - 1
g.done = true // åkte till slutstationen utan att kliva av
}
}
}