M10b: bussar, fler sidospår och överstimulerande UI
All checks were successful
release / build-release (push) Successful in 1m36s

Ett tåg kan vara en buss (vehicle: bus): samma tidtabell, faser,
bedömning i nätet och ombordstigning, men ett fordon på en väg i stället
för vagnar på räls, hållplatsskylt, "1番" på tavlan, tuta vid inrullning
och dörrsignal före avgång. Nätet har tre busslinjer från Narita. Nya
sidobanor: 001b Flygbussterminalen (biljettbana med tre bussar) och 003b
Harajuku (Yamanote åt rätt eller fel håll runt ringen). Overload-flaggan
ger en rullande reklamticker och maximalt med affischer (Ueno, Tokyo).
Romaji utan makroner (fonten saknar dem). Skärmdumparna som börjar på
bankartan går via titelskärmen. Tester, docs och wiki uppdaterade.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 17:33:55 +01:00
parent 87695b668b
commit 0c47b0ad00
35 changed files with 666 additions and 73 deletions

View File

@@ -194,6 +194,7 @@ type Train struct {
Correct bool `json:"correct"` // tar spelaren till rätt resmål (räknas ut från nätet när banan har station/to)
LineID string `json:"lineId"` // linjen i linjenätet (resources/data/network.json)
Terminus string `json:"terminus"` // en station i färdriktningen (nätets id) – avgör riktningen
Vehicle string `json:"vehicle"` // "" = tåg, "bus" = buss (väg i stället för räls, hållplats i stället för perrong)
Crowded bool `json:"crowded"` // fullt tåg: minispel för att få plats (gäller alla vagnar)
Cars int `json:"cars"` // antal vagnar (0 = DefaultCars)
WomenOnlyCar int `json:"womenOnlyCar"` // 1-baserat vagnnummer för kvinnovagn, 0 = ingen
@@ -269,6 +270,7 @@ type Level struct {
SeatSelection bool `json:"seatSelection"` // biljettköpet fortsätter med sittplatsval efter sällskapets önskemål
OneWayGates bool `json:"oneWayGates"` // spärren släpper inte ut en igen förrän man åkt; personal vid spärren låser upp (tar tid)
Melody string `json:"melody"` // stationens avgångsmelodi: "a".."d" (tomt = vald efter id)
Overload bool `json:"overload"` // överstimulerande UI: reklamticker, blinkande reklam, extra affischer
FareYen int `json:"fareYen"` // vad spärren drar från kortet (0 = standard)
Grid []string `json:"grid,omitempty"` // envåningsbana (kompatibilitet) – blir Floors[0]
Floors []Storey `json:"floors,omitempty"` // flervåningsbana; alla våningar lika stora
@@ -466,8 +468,14 @@ func (l *Level) Validate() error {
if tr.Correct {
correct++
}
if tr.Vehicle != "" && tr.Vehicle != "bus" {
return fmt.Errorf("level %s: tåg %d har okänt fordon %q (tomt eller bus)", l.ID, i, tr.Vehicle)
}
if tr.Cars == 0 {
tr.Cars = DefaultCars
if tr.IsBus() {
tr.Cars = 1
}
}
if tr.Cars < 1 || tr.Cars > MaxCars {
return fmt.Errorf("level %s: tåg %d har %d vagnar (1–%d)", l.ID, i, tr.Cars, MaxCars)
@@ -720,11 +728,18 @@ func (l *Level) Unreachable() []Point {
return out
}
// BoardText är avgångstavlans text på japanska, t.ex. "2番線 成田エクスプレス 新宿方面".
// BoardText är avgångstavlans text på japanska, t.ex. "2番線 成田エクスプレス 新宿方面"
// (för bussar "1番 リムジンバス 新宿方面": hållplatsnummer i stället för spår).
func (tr *Train) BoardText() string {
if tr.IsBus() {
return tr.Track + "番 " + tr.Line.Get("ja") + " " + tr.Destination.Get("ja") + "方面"
}
return tr.Track + "番線 " + tr.Line.Get("ja") + " " + tr.Destination.Get("ja") + "方面"
}
// IsBus rapporterar om fordonet är en buss.
func (tr *Train) IsBus() bool { return tr.Vehicle == "bus" }
// TrackTile är rutan "bortom perrongkanten" där rälsen går: första väggruta
// nedanför, till höger, till vänster eller ovanför perrongen. horizontal
// anger om rälsen (och tågets rörelse) går i sidled.

View File

@@ -838,3 +838,24 @@ func TestToiletSides(t *testing.T) {
}
}
}
func TestBusVehicleAndBoardText(t *testing.T) {
src := `{"id":"bus","name":{"sv":"x"},"difficulty":1,"destination":{"ja":"新宿"},
"grid":["#######","#S.GP.#","#######"],
"trains":[{"platform":"P","track":"1","line":{"ja":"リムジンバス"},"destination":{"ja":"新宿"},"correct":true,"vehicle":"bus"}]}`
l, err := Parse([]byte(src))
if err != nil {
t.Fatal(err)
}
tr := &l.Trains[0]
if !tr.IsBus() || tr.Cars != 1 || tr.BoardText() != "1番 リムジンバス 新宿方面" {
t.Fatalf("buss: cars=%d board=%q", tr.Cars, tr.BoardText())
}
if _, err := Parse([]byte(strings.Replace(src, `"vehicle":"bus"`, `"vehicle":"tram"`, 1))); err == nil {
t.Fatal("okänt fordon ska ge fel")
}
train, _ := Parse([]byte(strings.Replace(src, `,"vehicle":"bus"`, ``, 1)))
if train.Trains[0].IsBus() || train.Trains[0].Cars != DefaultCars || train.Trains[0].BoardText() != "1番線 リムジンバス 新宿方面" {
t.Fatal("utan vehicle är det ett tåg")
}
}