Texterna ryms på skärmen på svenska och engelska
All checks were successful
release / build-release (push) Successful in 1m37s
Björn såg att bankartans tangentrad klipptes på engelska. Nu mäts alla
enradstexter med den riktiga fonten i båda språken mot sitt utrymme
(game.TestSingleLineTextsFit) och HUD-raden för alla banor
(TestLevelNamesFitHUD). Åtgärder: kortare etiketter ("Mål"/"To"),
bankartans tangentrad på två rader (versionen står på titelskärmen),
HUD:en stryker japansk hint, "via" och valfria plånboksdelar som inte
får plats och kortar med "..", fler rutor radbryts, kortare bannamn,
kortare minispelstexter. Skärmdumpar på engelska (map-en, sign-en) via en
engelsk demosparfil. Alla skärmdumpar omtagna.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## Oreleaset
|
## Oreleaset
|
||||||
|
|
||||||
|
- Texterna ryms på skärmen på både svenska och engelska: kortare etiketter
|
||||||
|
("Mål"/"To"), bankartans tangentrad på två rader, HUD-raden stryker
|
||||||
|
delar som inte får plats, radbrytning i fler rutor; breddtester för alla
|
||||||
|
enradstexter med den riktiga fonten.
|
||||||
- Bussar: flygbussterminalen (sidobana 001b) med tre bussar på väg, egen
|
- Bussar: flygbussterminalen (sidobana 001b) med tre bussar på väg, egen
|
||||||
sprite, hållplatsskylt och tuta; sidobanan 003b Harajuku; överstimulerande
|
sprite, hållplatsskylt och tuta; sidobanan 003b Harajuku; överstimulerande
|
||||||
UI med reklamticker på Ueno och Tokyo.
|
UI med reklamticker på Ueno och Tokyo.
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
BIN
doc/img/bus.png
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
BIN
doc/img/cars.png
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
BIN
doc/img/map-en.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
BIN
doc/img/map.png
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
BIN
doc/img/ride.png
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
BIN
doc/img/shop.png
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
BIN
doc/img/sign-en.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
BIN
doc/img/sign.png
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
BIN
doc/img/tips.png
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -139,6 +139,7 @@ att bygga på Linux som på Windows.
|
|||||||
| `internal/endless` | `TestPlanIsDeterministicAndValid`, `TestDestinationsNeedTrapAndRoute` | **M11:** samma seed → samma följd, annan seed → annan; varje mål har 1–4 rätta tåg och en fälla, nytt resmål, inga byten, aldrig samma station två gånger i rad; sällskapet växer och tidsgränsen krymper |
|
| `internal/endless` | `TestPlanIsDeterministicAndValid`, `TestDestinationsNeedTrapAndRoute` | **M11:** samma seed → samma följd, annan seed → annan; varje mål har 1–4 rätta tåg och en fälla, nytt resmål, inga byten, aldrig samma station två gånger i rad; sällskapet växer och tidsgränsen krymper |
|
||||||
| `internal/progress` | `TestNewGamePlusKeepsSkillsResetsItems` | **M11:** NG+ behåller skills, XP, skillpoäng och klarad story; nollställer items, utrustning och banresultat |
|
| `internal/progress` | `TestNewGamePlusKeepsSkillsResetsItems` | **M11:** NG+ behåller skills, XP, skillpoäng och klarad story; nollställer items, utrustning och banresultat |
|
||||||
| `internal/save` | `TestEndlessRecords` | **M11:** poänglistan sorterad, kapad till fem, "ny bästa" bara när toppen slås |
|
| `internal/save` | `TestEndlessRecords` | **M11:** poänglistan sorterad, kapad till fem, "ny bästa" bara när toppen slås |
|
||||||
|
| `internal/game` | `TestSingleLineTextsFit`, `TestLevelNamesFitHUD` | **Textbredd (Björn 2026-10-25):** alla texter som ritas på en rad mäts med den riktiga fonten på svenska och engelska mot sitt utrymme (skärm, panel, pausmeny, inställningsrad, bankartans sammansatta rader); bannamn + resmål + mätare ryms på HUD:ens första rad för alla banor. `TestReportTextWidths -v` listar allt över skärmbredden |
|
||||||
| `internal/game` | `TestHighlightDestination` | **M13:** resmålets tecken får markeringsfärg, resten nedtonas, flera träffar, ingen träff |
|
| `internal/game` | `TestHighlightDestination` | **M13:** resmålets tecken får markeringsfärg, resten nedtonas, flera träffar, ingen träff |
|
||||||
| `internal/game` | `TestAllSoundsDecode`, `TestMelodyForIsStableAndKnown` | **M12:** alla ljud i `AllSounds` finns, avkodas och är minst 50 ms; melodin per bana är stabil och känd; ljudlagret är nil-säkert |
|
| `internal/game` | `TestAllSoundsDecode`, `TestMelodyForIsStableAndKnown` | **M12:** alla ljud i `AllSounds` finns, avkodas och är minst 50 ms; melodin per bana är stabil och känd; ljudlagret är nil-säkert |
|
||||||
| `internal/save` | `TestSettingsDefaultsAndNormalize` | **M12:** standardinställningar, klampning av volymer/skala, okänt språk blir sv, saknade inställningar fylls i |
|
| `internal/save` | `TestSettingsDefaultsAndNormalize` | **M12:** standardinställningar, klampning av volymer/skala, okänt språk blir sv, saknade inställningar fylls i |
|
||||||
@@ -181,6 +182,7 @@ att bygga på Linux som på Windows.
|
|||||||
En milstolpe är klar när:
|
En milstolpe är klar när:
|
||||||
|
|
||||||
1. Alla tester i raden ovan finns och är gröna.
|
1. Alla tester i raden ovan finns och är gröna.
|
||||||
|
1. Ingen text går utanför skärmen på svenska eller engelska – nya enradstexter läggs till i `TestSingleLineTextsFit`, och långa texter radbryts med `wrapLine`.
|
||||||
2. `go vet ./...` och `go test -race ./...` är gröna.
|
2. `go vet ./...` och `go test -race ./...` är gröna.
|
||||||
3. Debug- och release-bygget går igenom för Linux och Windows.
|
3. Debug- och release-bygget går igenom för Linux och Windows.
|
||||||
4. Roadmapen är uppdaterad med datum och eventuella avsteg.
|
4. Roadmapen är uppdaterad med datum och eventuella avsteg.
|
||||||
|
|||||||
@@ -416,13 +416,6 @@ func (s *LevelScene) drawHUD(g *Game, screen *ebiten.Image) {
|
|||||||
a := g.Assets
|
a := g.Assets
|
||||||
vector.DrawFilledRect(screen, 0, 0, ScreenW, 24, color.RGBA{24, 20, 37, 200}, false)
|
vector.DrawFilledRect(screen, 0, 0, ScreenW, 24, color.RGBA{24, 20, 37, 200}, false)
|
||||||
|
|
||||||
dest := r.FinalDestination().Get("romaji")
|
|
||||||
if !r.IsLastLeg() { // resa med byte: den här delbanan slutar vid bytesstationen
|
|
||||||
dest += " " + a.T("hud.via") + " " + r.Level.Destination.Get("romaji")
|
|
||||||
}
|
|
||||||
if r.Know.ShowsDestinationJP() { // hint från nivå 1: delbanans mål på japanska, att matcha mot skyltarna
|
|
||||||
dest += " " + r.Level.Destination.Get("ja")
|
|
||||||
}
|
|
||||||
name := r.Level.Name.Get(UILang)
|
name := r.Level.Name.Get(UILang)
|
||||||
if r.Level.NumFloors() > 1 {
|
if r.Level.NumFloors() > 1 {
|
||||||
name += " " + r.Level.FloorName(r.Player.Tile.Z)
|
name += " " + r.Level.FloorName(r.Player.Tile.Z)
|
||||||
@@ -433,39 +426,72 @@ func (s *LevelScene) drawHUD(g *Game, screen *ebiten.Image) {
|
|||||||
if s.endless && g.Endless != nil {
|
if s.endless && g.Endless != nil {
|
||||||
name = fmt.Sprintf("#%d %s", g.Endless.Step+1, name)
|
name = fmt.Sprintf("#%d %s", g.Endless.Step+1, name)
|
||||||
}
|
}
|
||||||
|
metersX := ScreenW - 4 - len(r.Party.Members)*(meterW+meterGap)
|
||||||
|
name = a.fitText(name, metersX/2)
|
||||||
a.Draw(screen, name, 4, 0, colText)
|
a.Draw(screen, name, 4, 0, colText)
|
||||||
destX := 4 + a.Width(name) + 12
|
destX := 4 + a.Width(name) + 12
|
||||||
a.Draw(screen, a.T("hud.destination")+": "+dest, destX, 0, colKnown)
|
// Resmålet: slutmålet, "via" bytesstationen och japanska hinten – i den
|
||||||
// Japanska-nivån får plats till vänster om mätarna; annars kortas den.
|
// ordningen stryks delar som inte får plats före mätarna.
|
||||||
jpX := destX + a.Width(a.T("hud.destination")+": "+dest) + 12
|
final := r.FinalDestination().Get("romaji")
|
||||||
metersX := ScreenW - 4 - len(r.Party.Members)*(meterW+meterGap)
|
via, jp := "", ""
|
||||||
jp := fmt.Sprintf("%s Lv%d·%d", a.T("hud.japanese"), r.Know.Level(), r.Know.Count())
|
if !r.IsLastLeg() { // resa med byte: den här delbanan slutar vid bytesstationen
|
||||||
if jpX+a.Width(jp) > metersX-4 {
|
via = " " + a.T("hud.via") + " " + r.Level.Destination.Get("romaji")
|
||||||
jp = fmt.Sprintf("Lv%d", r.Know.Level())
|
|
||||||
}
|
}
|
||||||
if jpX+a.Width(jp) <= metersX-4 {
|
if r.Know.ShowsDestinationJP() { // hint från nivå 1: delbanans mål på japanska, att matcha mot skyltarna
|
||||||
a.Draw(screen, jp, jpX, 0, colDim)
|
jp = " " + r.Level.Destination.Get("ja")
|
||||||
|
}
|
||||||
|
label := a.T("hud.destination") + ": "
|
||||||
|
dest := label + final + via + jp
|
||||||
|
for _, cand := range []string{label + final + via + jp, label + final + via, label + final + jp, label + final} {
|
||||||
|
dest = cand
|
||||||
|
if destX+a.Width(cand) <= metersX-4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dest = a.fitText(dest, metersX-4-destX)
|
||||||
|
a.Draw(screen, dest, destX, 0, colKnown)
|
||||||
|
// Japanska-nivån får plats till vänster om mätarna; annars kortas den.
|
||||||
|
jpX := destX + a.Width(dest) + 12
|
||||||
|
lv := fmt.Sprintf("%s Lv%d·%d", a.T("hud.japanese"), r.Know.Level(), r.Know.Count())
|
||||||
|
if jpX+a.Width(lv) > metersX-4 {
|
||||||
|
lv = fmt.Sprintf("Lv%d", r.Know.Level())
|
||||||
|
}
|
||||||
|
if jpX+a.Width(lv) <= metersX-4 {
|
||||||
|
a.Draw(screen, lv, jpX, 0, colDim)
|
||||||
}
|
}
|
||||||
timeStr := fmt.Sprintf("%s %3.0fs", a.T("hud.time"), r.Time)
|
timeStr := fmt.Sprintf("%s %3.0fs", a.T("hud.time"), r.Time)
|
||||||
if r.TimeLimit() > 0 {
|
if r.TimeLimit() > 0 {
|
||||||
left := r.TimeLeft()
|
left := r.TimeLeft()
|
||||||
timeStr = fmt.Sprintf("%s %d:%02d", a.T("hud.time_left"), int(left)/60, int(left)%60)
|
timeStr = fmt.Sprintf("%s %d:%02d", a.T("hud.time_left"), int(left)/60, int(left)%60)
|
||||||
}
|
}
|
||||||
wallet := fmt.Sprintf("%s %s %d¥ %s %d¥", timeStr, a.T("hud.card"), r.Wallet.Card, a.T("hud.cash_short"), r.Wallet.Cash)
|
// Plånboksraden: tid, kort, kontanter, sedan valfria delar (missade tåg,
|
||||||
|
// biljett, telefon) som stryks bakifrån om de inte får plats före mätarna.
|
||||||
|
must := fmt.Sprintf("%s %s %d¥ %s %d¥", timeStr, a.T("hud.card"), r.Wallet.Card, a.T("hud.cash_short"), r.Wallet.Cash)
|
||||||
|
var optional []string
|
||||||
if r.MaxMissed() > 0 {
|
if r.MaxMissed() > 0 {
|
||||||
wallet += fmt.Sprintf(" %s %d/%d", a.T("hud.missed_short"), r.MissedTrains, r.MaxMissed())
|
optional = append(optional, fmt.Sprintf("%s %d/%d", a.T("hud.missed_short"), r.MissedTrains, r.MaxMissed()))
|
||||||
}
|
|
||||||
if r.Wallet.Phone {
|
|
||||||
wallet += fmt.Sprintf(" %s %.0f%%", a.T("hud.phone"), r.Wallet.Battery)
|
|
||||||
}
|
}
|
||||||
if r.Wallet.Ticket != "" {
|
if r.Wallet.Ticket != "" {
|
||||||
wallet += " " + a.T("hud.ticket") + " " + r.Wallet.Ticket
|
optional = append(optional, a.T("hud.ticket")+" "+r.Wallet.Ticket)
|
||||||
|
}
|
||||||
|
if r.Wallet.Phone {
|
||||||
|
optional = append(optional, fmt.Sprintf("%s %.0f%%", a.T("hud.phone"), r.Wallet.Battery))
|
||||||
|
}
|
||||||
|
wallet := must
|
||||||
|
for n := len(optional); n >= 0; n-- {
|
||||||
|
wallet = must
|
||||||
|
for _, o := range optional[:n] {
|
||||||
|
wallet += " " + o
|
||||||
|
}
|
||||||
|
if 4+a.Width(wallet) <= metersX-4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
walletCol := colDim
|
walletCol := colDim
|
||||||
if r.TimeLimit() > 0 && r.TimeLeft() < 30 {
|
if r.TimeLimit() > 0 && r.TimeLeft() < 30 {
|
||||||
walletCol = colBad
|
walletCol = colBad
|
||||||
}
|
}
|
||||||
a.Draw(screen, wallet, 4, LineH, walletCol)
|
a.Draw(screen, a.fitText(wallet, metersX-8), 4, LineH, walletCol)
|
||||||
s.drawMeters(g, screen)
|
s.drawMeters(g, screen)
|
||||||
|
|
||||||
if r.Outcome != sim.Ongoing || r.Mini != nil || s.shop != nil {
|
if r.Outcome != sim.Ongoing || r.Mini != nil || s.shop != nil {
|
||||||
@@ -634,9 +660,9 @@ func (s *LevelScene) infoLines(a *Assets) (lines []line, accent color.Color, bar
|
|||||||
case r.NoExitAt >= 0 && r.Time-r.NoExitAt < deniedShowSec:
|
case r.NoExitAt >= 0 && r.Time-r.NoExitAt < deniedShowSec:
|
||||||
return a.wrapLine(plainLine(fmt.Sprintf(a.T("hud.no_exit"), sim.StaffSec), colBad), maxW), colBad, false
|
return a.wrapLine(plainLine(fmt.Sprintf(a.T("hud.no_exit"), sim.StaffSec), colBad), maxW), colBad, false
|
||||||
case r.NeedsStaff():
|
case r.NeedsStaff():
|
||||||
return []line{plainLine(fmt.Sprintf(a.T("hud.staff_prompt"), sim.StaffSec), colWarn)}, colWarn, false
|
return a.wrapLine(plainLine(fmt.Sprintf(a.T("hud.staff_prompt"), sim.StaffSec), colWarn), maxW), colWarn, false
|
||||||
case lvl.OneWayGates && r.StaffUnlocked:
|
case lvl.OneWayGates && r.StaffUnlocked:
|
||||||
return []line{plainLine(a.T("hud.unlocked_prompt"), colGood)}, colGood, false
|
return a.wrapLine(plainLine(a.T("hud.unlocked_prompt"), colGood), maxW), colGood, false
|
||||||
}
|
}
|
||||||
case level.Toilet:
|
case level.Toilet:
|
||||||
if r.Facility.Active {
|
if r.Facility.Active {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||||
|
|
||||||
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/buildinfo"
|
|
||||||
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level"
|
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -266,7 +265,7 @@ func (m *MapScene) Draw(g *Game, screen *ebiten.Image) {
|
|||||||
a.Draw(screen, a.T("hud.debuffs")+": "+strings.Join(parts, ", "), 8, y, colBad)
|
a.Draw(screen, a.T("hud.debuffs")+": "+strings.Join(parts, ", "), 8, y, colBad)
|
||||||
}
|
}
|
||||||
a.Draw(screen, a.T("map.hint"), 8, ScreenH-24, colDim)
|
a.Draw(screen, a.T("map.hint"), 8, ScreenH-24, colDim)
|
||||||
a.Draw(screen, fmt.Sprintf("%s (%s)", buildinfo.Version, buildinfo.Mode), 8, ScreenH-12, colDim)
|
a.Draw(screen, a.T("map.hint2"), 8, ScreenH-12, colDim) // versionen står på titelskärmen
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawLine ritar en tjock linje mellan två punkter.
|
// drawLine ritar en tjock linje mellan två punkter.
|
||||||
|
|||||||
@@ -123,8 +123,10 @@ func (r *ResultScene) Draw(g *Game, screen *ebiten.Image) {
|
|||||||
if len(run.LearnedDest) > 0 {
|
if len(run.LearnedDest) > 0 {
|
||||||
msg += " " + a.T("hud.learned") + ": " + strings.Join(run.LearnedDest, " ") + " (" + a.T("result.destination") + ")"
|
msg += " " + a.T("hud.learned") + ": " + strings.Join(run.LearnedDest, " ") + " (" + a.T("result.destination") + ")"
|
||||||
}
|
}
|
||||||
a.Draw(screen, msg, 28, y, colKnown)
|
for _, l := range a.wrapLine(plainLine(msg, colKnown), ScreenW-56) {
|
||||||
y += LineH
|
a.drawLines(screen, []line{l}, 28, y)
|
||||||
|
y += LineH
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if o := r.from.outcome; run.Outcome == sim.Won {
|
if o := r.from.outcome; run.Outcome == sim.Won {
|
||||||
prog := fmt.Sprintf("+%d %s", o.XP, a.T("map.xp"))
|
prog := fmt.Sprintf("+%d %s", o.XP, a.T("map.xp"))
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ func (s *TitleScene) Draw(g *Game, screen *ebiten.Image) {
|
|||||||
y += LineH
|
y += LineH
|
||||||
}
|
}
|
||||||
if s.confirm {
|
if s.confirm {
|
||||||
a.Draw(screen, a.T("title.quit_confirm"), 150, y-LineH, colWarn)
|
a.Draw(screen, a.T("title.quit_confirm"), 16, y+2, colWarn)
|
||||||
}
|
}
|
||||||
a.Draw(screen, a.T("title.hint"), 8, ScreenH-12, colDim)
|
a.Draw(screen, a.T("title.hint"), 8, ScreenH-12, colDim)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,21 @@ func (a *Assets) drawLines(dst drawTarget, lines []line, x, y int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fitText kortar en text med ".." tills den ryms i maxW pixlar.
|
||||||
|
func (a *Assets) fitText(s string, maxW int) string {
|
||||||
|
if a.Width(s) <= maxW {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
r := []rune(s)
|
||||||
|
for len(r) > 0 {
|
||||||
|
r = r[:len(r)-1]
|
||||||
|
if a.Width(string(r)+"..") <= maxW {
|
||||||
|
return string(r) + ".."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// highlightLine är tutorialens sätt att läsa en skylt: resmålets japanska
|
// highlightLine är tutorialens sätt att läsa en skylt: resmålets japanska
|
||||||
// tecken i markeringsfärg, allt annat nedtonat.
|
// tecken i markeringsfärg, allt annat nedtonat.
|
||||||
func highlightLine(text, needle string, hi, dim color.Color) line {
|
func highlightLine(text, needle string, hi, dim color.Color) line {
|
||||||
|
|||||||
156
internal/game/textwidth_test.go
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/text/v2"
|
||||||
|
|
||||||
|
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level"
|
||||||
|
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/res"
|
||||||
|
)
|
||||||
|
|
||||||
|
// loadTexts läser ett språks UI-texter.
|
||||||
|
func loadTexts(t *testing.T, lang string) map[string]string {
|
||||||
|
t.Helper()
|
||||||
|
m := map[string]string{}
|
||||||
|
if err := res.LoadJSON(&m, "text", lang+".json"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func uiFace(t *testing.T) *text.GoTextFace {
|
||||||
|
t.Helper()
|
||||||
|
face, err := loadFont("PixelMplus10-Regular.ttf", 10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return face
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestSingleLineTextsFit: texter som ritas på en rad utan radbrytning ska
|
||||||
|
// rymmas i sitt utrymme på båda språken. Bredderna mäts med den riktiga
|
||||||
|
// fonten. Texter som radbryts vid visning (tutorial, tips, premiss …) ingår
|
||||||
|
// inte.
|
||||||
|
func TestSingleLineTextsFit(t *testing.T) {
|
||||||
|
face := uiFace(t)
|
||||||
|
width := func(s string) int { return int(text.Advance(s, face)) }
|
||||||
|
const (
|
||||||
|
full = ScreenW - 16 // en rad över hela skärmen med marginal
|
||||||
|
panel = ScreenW - 24 - 16 // minispelspanelens innerbredd
|
||||||
|
resW = ScreenW - 56 // resultatpanelen
|
||||||
|
pause = ScreenW - 96 // pausmenyn
|
||||||
|
)
|
||||||
|
type check struct {
|
||||||
|
key string
|
||||||
|
max int
|
||||||
|
args []any
|
||||||
|
}
|
||||||
|
checks := []check{
|
||||||
|
{"map.hint", full, nil}, {"map.hint2", full, nil}, {"menu.hint", full, nil}, {"settings.hint", full, nil},
|
||||||
|
{"title.hint", full, nil}, {"tutorial.hint", full, nil}, {"ending.hint", full, nil},
|
||||||
|
{"result.hint", resW, nil}, {"result.hint_endless", resW, nil}, {"result.hint_endless_over", resW, nil},
|
||||||
|
{"pause.hint", pause, nil}, {"pause.quit_confirm", pause, nil}, {"pause.resume", pause, nil}, {"pause.leave", pause, nil}, {"pause.quit", pause, nil},
|
||||||
|
{"title.quit_confirm", ScreenW - 32, nil}, {"title.tutorial", 110, nil}, {"menu.settings", 110, nil}, {"menu.quit", 110, nil}, {"menu.continue", 110, nil}, {"menu.start", 110, nil},
|
||||||
|
{"items.hint", full, nil}, {"skills.hint", full, nil}, {"words.hint", full, nil}, {"shop.hint", panel, nil},
|
||||||
|
{"mini.car.hint", panel, nil}, {"mini.car.pick", panel, nil}, {"mini.car.alighting", panel, nil}, {"mini.car.men", panel, nil},
|
||||||
|
{"mini.crowd.title", panel - 60, nil}, {"mini.crowd.hint", panel, []any{2}},
|
||||||
|
{"mini.ride.title", panel - 60, nil}, {"mini.ride.hint", panel, nil}, {"mini.ride.let_pass", panel, nil},
|
||||||
|
{"mini.ticket.title", panel - 60, nil}, {"mini.ticket.hint", panel, []any{"Narita Airport", 5000}},
|
||||||
|
{"mini.seat.hint", panel, []any{"Ville"}}, {"mini.seat.prefs", panel, nil},
|
||||||
|
{"mini.topup.title", panel - 60, nil}, {"mini.topup.feed", panel, []any{5000}}, {"mini.topup.pick", panel, nil}, {"mini.topup.amount", panel, nil}, {"mini.topup.member", panel, nil}, {"mini.topup.wrong", panel - 60, nil},
|
||||||
|
{"mini.payer.title", panel - 60, nil}, {"mini.payer.price", panel, []any{900}}, {"mini.payer.short", panel, nil}, {"mini.payer.hint", panel, nil},
|
||||||
|
{"hud.kiosk_prompt", full - 60, []any{4, 800}}, {"hud.toilet_prompt", full, []any{4, 32.0}}, {"hud.machine_topup", full, []any{100000, 100000}}, {"hud.machine_ticket", full, []any{100000}},
|
||||||
|
{"hud.help_prompt", full, []any{30.0}}, {"hud.train_near", full, nil}, {"hud.staff_busy", full, nil}, {"hud.toilet_busy", full, nil}, {"hud.kiosk_none", full - 60, nil}, {"hud.toilet_none", full, nil},
|
||||||
|
{"settings.master", 118, nil}, {"settings.effects", 118, nil}, {"settings.music", 118, nil}, {"settings.lang", 118, nil}, {"settings.scale", 118, nil}, {"settings.fullscreen", 118, nil},
|
||||||
|
{"settings.scale_auto", ScreenW - 130 - 8, nil},
|
||||||
|
{"ending.summary", full, nil}, {"ending.story_done", full * 2, nil},
|
||||||
|
}
|
||||||
|
for _, lang := range []string{"sv", "en"} {
|
||||||
|
m := loadTexts(t, lang)
|
||||||
|
for _, c := range checks {
|
||||||
|
s, ok := m[c.key]
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%s: nyckeln %s saknas", lang, c.key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(c.args) > 0 {
|
||||||
|
s = fmt.Sprintf(s, c.args...)
|
||||||
|
}
|
||||||
|
if w := width(s); w > c.max {
|
||||||
|
t.Errorf("%s %s är %d px, får vara %d: %q", lang, c.key, w, c.max, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Bankartans sammansatta rader.
|
||||||
|
line2 := fmt.Sprintf(m["menu.phone"], m["menu.on"]) + " " + fmt.Sprintf(m["menu.mode"], m["mode.signs"])
|
||||||
|
if w := width(line2); w > full {
|
||||||
|
t.Errorf("%s bankartans lägesrad är %d px: %q", lang, w, line2)
|
||||||
|
}
|
||||||
|
endless := m["map.endless"] + " (" + fmt.Sprintf(m["map.endless_best"], 99999, 99) + ") " + m["map.ngplus"] + " (" + fmt.Sprintf(m["map.ngplus_level"], 9) + ")"
|
||||||
|
if w := width(endless); w > full {
|
||||||
|
t.Errorf("%s bankartans oändligt-rad är %d px: %q", lang, w, endless)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLevelNamesFitHUD: bannamnet, resmålet och mätarna ska rymmas på
|
||||||
|
// HUD:ens första rad även med fullt sällskap och byte, på båda språken.
|
||||||
|
func TestLevelNamesFitHUD(t *testing.T) {
|
||||||
|
face := uiFace(t)
|
||||||
|
width := func(s string) int { return int(text.Advance(s, face)) }
|
||||||
|
dir, err := res.Path("data", "levels")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
all, err := level.LoadAll(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, lang := range []string{"sv", "en"} {
|
||||||
|
m := loadTexts(t, lang)
|
||||||
|
for _, l := range all {
|
||||||
|
name := l.Name.Get(lang)
|
||||||
|
if l.NumFloors() > 1 {
|
||||||
|
name += " " + l.FloorName(0)
|
||||||
|
}
|
||||||
|
chain := level.ChainOf(all, l.ID)
|
||||||
|
if len(chain) > 1 {
|
||||||
|
name += " 1/2"
|
||||||
|
}
|
||||||
|
dest := m["hud.destination"] + ": " + level.FinalOf(all, l).Destination.Get("romaji")
|
||||||
|
if len(chain) > 1 {
|
||||||
|
dest += " " + m["hud.via"] + " " + l.Destination.Get("romaji")
|
||||||
|
}
|
||||||
|
metersX := ScreenW - 4 - max(1, l.Party)*(meterW+meterGap)
|
||||||
|
if w := 4 + width(name) + 12 + width(dest); w > metersX-4 {
|
||||||
|
t.Errorf("%s %s: HUD-raden %q + %q är %d px, mätarna börjar vid %d", lang, l.ID, name, dest, w, metersX)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestReportTextWidths skriver (med -v) ut alla texter bredare än skärmen,
|
||||||
|
// som hjälp vid textgranskning. Fäller inget: de flesta radbryts vid visning.
|
||||||
|
func TestReportTextWidths(t *testing.T) {
|
||||||
|
face := uiFace(t)
|
||||||
|
for _, lang := range []string{"sv", "en"} {
|
||||||
|
m := loadTexts(t, lang)
|
||||||
|
var keys []string
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
for _, k := range keys {
|
||||||
|
for _, line := range strings.Split(m[k], "\n") {
|
||||||
|
if w := int(text.Advance(line, face)); w > ScreenW-16 {
|
||||||
|
t.Logf("WIDE %s %-28s %4d px %q", lang, k, w, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = filepath.Base
|
||||||
|
}
|
||||||
@@ -33,9 +33,9 @@
|
|||||||
"id": "001b-flygbussen",
|
"id": "001b-flygbussen",
|
||||||
"kind": "side",
|
"kind": "side",
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Airport bus terminal",
|
"en": "Airport buses",
|
||||||
"ja": "空港バスのりば",
|
"ja": "空港バスのりば",
|
||||||
"sv": "Flygbussterminalen"
|
"sv": "Flygbussarna"
|
||||||
},
|
},
|
||||||
"order": 1,
|
"order": 1,
|
||||||
"parent": "001-flygplatsen",
|
"parent": "001-flygplatsen",
|
||||||
@@ -57,9 +57,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": {
|
"text": {
|
||||||
"en": "\u003c- Stop 2 limousine bus for Yokohama Stop 1 limousine bus for Shinjuku -\u003e",
|
"en": "<- Stop 2 limousine bus for Yokohama Stop 1 limousine bus for Shinjuku ->",
|
||||||
"ja": "← 2番 リムジンバス 横浜方面 1番 リムジンバス 新宿方面 →",
|
"ja": "← 2番 リムジンバス 横浜方面 1番 リムジンバス 新宿方面 →",
|
||||||
"sv": "\u003c- Hållplats 2 flygbuss mot Yokohama Hållplats 1 flygbuss mot Shinjuku -\u003e"
|
"sv": "<- Hållplats 2 flygbuss mot Yokohama Hållplats 1 flygbuss mot Shinjuku ->"
|
||||||
},
|
},
|
||||||
"x": 3,
|
"x": 3,
|
||||||
"y": 3,
|
"y": 3,
|
||||||
|
|||||||
@@ -34,9 +34,9 @@
|
|||||||
"kind": "main",
|
"kind": "main",
|
||||||
"maxMissedTrains": 2,
|
"maxMissedTrains": 2,
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Shinjuku Station",
|
"en": "Shinjuku",
|
||||||
"ja": "新宿駅",
|
"ja": "新宿駅",
|
||||||
"sv": "Shinjuku station"
|
"sv": "Shinjuku"
|
||||||
},
|
},
|
||||||
"order": 2,
|
"order": 2,
|
||||||
"party": 3,
|
"party": 3,
|
||||||
@@ -62,9 +62,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": {
|
"text": {
|
||||||
"en": "\u003c- Track 8 Chuo Line (Tokyo) Track 14 Yamanote Line (Shibuya) -\u003e",
|
"en": "<- Track 8 Chuo Line (Tokyo) Track 14 Yamanote Line (Shibuya) ->",
|
||||||
"ja": "← 8番線 中央線 東京方面 14番線 山手線 渋谷方面 →",
|
"ja": "← 8番線 中央線 東京方面 14番線 山手線 渋谷方面 →",
|
||||||
"sv": "\u003c- Spår 8 Chuo-linjen (Tokyo) Spår 14 Yamanote-linjen (Shibuya) -\u003e"
|
"sv": "<- Spår 8 Chuo-linjen (Tokyo) Spår 14 Yamanote-linjen (Shibuya) ->"
|
||||||
},
|
},
|
||||||
"x": 11,
|
"x": 11,
|
||||||
"y": 15,
|
"y": 15,
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
"kind": "main",
|
"kind": "main",
|
||||||
"order": 3,
|
"order": 3,
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Shibuya Station",
|
"en": "Shibuya",
|
||||||
"ja": "渋谷駅",
|
"ja": "渋谷駅",
|
||||||
"sv": "Shibuya station"
|
"sv": "Shibuya"
|
||||||
},
|
},
|
||||||
"difficulty": 3,
|
"difficulty": 3,
|
||||||
"party": 4,
|
"party": 4,
|
||||||
|
|||||||
@@ -65,9 +65,9 @@
|
|||||||
"kind": "main",
|
"kind": "main",
|
||||||
"maxMissedTrains": 2,
|
"maxMissedTrains": 2,
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Tokyo Station",
|
"en": "Tokyo",
|
||||||
"ja": "東京駅",
|
"ja": "東京駅",
|
||||||
"sv": "Tokyo station"
|
"sv": "Tokyo"
|
||||||
},
|
},
|
||||||
"order": 4,
|
"order": 4,
|
||||||
"party": 3,
|
"party": 3,
|
||||||
@@ -92,9 +92,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": {
|
"text": {
|
||||||
"en": "\u003c- Track 7 Tokaido Line (Shinagawa) Track 4 Yamanote Line (Ueno) -\u003e",
|
"en": "<- Track 7 Tokaido Line (Shinagawa) Track 4 Yamanote Line (Ueno) ->",
|
||||||
"ja": "← 7番線 東海道線 品川方面 4番線 山手線 上野方面 →",
|
"ja": "← 7番線 東海道線 品川方面 4番線 山手線 上野方面 →",
|
||||||
"sv": "\u003c- Spår 7 Tokaido-linjen (Shinagawa) Spår 4 Yamanote-linjen (Ueno) -\u003e"
|
"sv": "<- Spår 7 Tokaido-linjen (Shinagawa) Spår 4 Yamanote-linjen (Ueno) ->"
|
||||||
},
|
},
|
||||||
"x": 3,
|
"x": 3,
|
||||||
"y": 3,
|
"y": 3,
|
||||||
|
|||||||
@@ -36,9 +36,9 @@
|
|||||||
"kind": "main",
|
"kind": "main",
|
||||||
"maxMissedTrains": 2,
|
"maxMissedTrains": 2,
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Ueno Station",
|
"en": "Ueno",
|
||||||
"ja": "上野駅",
|
"ja": "上野駅",
|
||||||
"sv": "Ueno station"
|
"sv": "Ueno"
|
||||||
},
|
},
|
||||||
"noise": 2,
|
"noise": 2,
|
||||||
"oneWayGates": true,
|
"oneWayGates": true,
|
||||||
|
|||||||
@@ -36,9 +36,9 @@
|
|||||||
"kind": "main",
|
"kind": "main",
|
||||||
"maxMissedTrains": 2,
|
"maxMissedTrains": 2,
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Ikebukuro Station",
|
"en": "Ikebukuro",
|
||||||
"ja": "池袋駅",
|
"ja": "池袋駅",
|
||||||
"sv": "Ikebukuro station"
|
"sv": "Ikebukuro"
|
||||||
},
|
},
|
||||||
"noise": 1,
|
"noise": 1,
|
||||||
"order": 6,
|
"order": 6,
|
||||||
@@ -62,9 +62,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": {
|
"text": {
|
||||||
"en": "\u003c- Track 2 Saikyo Line for Omiya Track 1 Saikyo Line for Osaki -\u003e",
|
"en": "<- Track 2 Saikyo Line for Omiya Track 1 Saikyo Line for Osaki ->",
|
||||||
"ja": "← 2番線 埼京線 大宮方面 1番線 埼京線 大崎方面 →",
|
"ja": "← 2番線 埼京線 大宮方面 1番線 埼京線 大崎方面 →",
|
||||||
"sv": "\u003c- Spår 2 Saikyo-linjen mot Omiya Spår 1 Saikyo-linjen mot Osaki -\u003e"
|
"sv": "<- Spår 2 Saikyo-linjen mot Omiya Spår 1 Saikyo-linjen mot Osaki ->"
|
||||||
},
|
},
|
||||||
"x": 17,
|
"x": 17,
|
||||||
"y": 9,
|
"y": 9,
|
||||||
|
|||||||
@@ -37,9 +37,9 @@
|
|||||||
"kind": "main",
|
"kind": "main",
|
||||||
"maxMissedTrains": 3,
|
"maxMissedTrains": 3,
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Akihabara Station",
|
"en": "Akihabara",
|
||||||
"ja": "秋葉原駅",
|
"ja": "秋葉原駅",
|
||||||
"sv": "Akihabara station"
|
"sv": "Akihabara"
|
||||||
},
|
},
|
||||||
"next": "007b-ueno",
|
"next": "007b-ueno",
|
||||||
"noise": 1,
|
"noise": 1,
|
||||||
@@ -65,9 +65,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": {
|
"text": {
|
||||||
"en": "\u003c- Track 3 Hibiya Line for Naka-Meguro Track 4 Hibiya Line for Kita-Senju -\u003e",
|
"en": "<- Track 3 Hibiya Line for Naka-Meguro Track 4 Hibiya Line for Kita-Senju ->",
|
||||||
"ja": "← 3番線 日比谷線 中目黒方面 4番線 日比谷線 北千住方面 →",
|
"ja": "← 3番線 日比谷線 中目黒方面 4番線 日比谷線 北千住方面 →",
|
||||||
"sv": "\u003c- Spår 3 Hibiya-linjen mot Naka-Meguro Spår 4 Hibiya-linjen mot Kita-Senju -\u003e"
|
"sv": "<- Spår 3 Hibiya-linjen mot Naka-Meguro Spår 4 Hibiya-linjen mot Kita-Senju ->"
|
||||||
},
|
},
|
||||||
"x": 9,
|
"x": 9,
|
||||||
"y": 11,
|
"y": 11,
|
||||||
|
|||||||
@@ -11,23 +11,23 @@
|
|||||||
"fareYen": 170,
|
"fareYen": 170,
|
||||||
"grid": [
|
"grid": [
|
||||||
"#################################",
|
"#################################",
|
||||||
"#S.#.i\u003c..\u003c..\u003c..\u003c..\u003c..\u003c..\u003c..\u003c..###",
|
"#S.#.i<..<..<..<..<..<..<..<..###",
|
||||||
"#..#..\u003e..\u003e..\u003e..\u003e..\u003e..\u003e..\u003e..\u003e..###",
|
"#..#..>..>..>..>..>..>..>..>..###",
|
||||||
"#\u003c\u003e################\u003c\u003e#######\u003c\u003e###",
|
"#<>################<>#######<>###",
|
||||||
"#..#..\u003c..\u003c..\u003c..\u003c..#..#..\u003c..#..###",
|
"#..#..<..<..<..<..#..#..<..#..###",
|
||||||
"#..#..\u003e..\u003e..\u003e..\u003e..#..#..\u003e..#..###",
|
"#..#..>..>..>..>..#..#..>..#..###",
|
||||||
"#\u003c\u003e#\u003c\u003e##########\u003c\u003e#\u003c\u003e#\u003c\u003e####\u003c\u003e###",
|
"#<>#<>##########<>#<>#<>####<>###",
|
||||||
"#..\u003c..#..\u003c..\u003c..#..#..#..\u003c..\u003c..###",
|
"#..<..#..<..<..#..#..#..<..<..###",
|
||||||
"#..\u003e..#..\u003e..\u003e..#..#..#..\u003e..\u003e..###",
|
"#..>..#..>..>..#..#..#..>..>..###",
|
||||||
"####\u003c\u003e#\u003c\u003e#\u003c\u003e#\u003c\u003e#\u003c\u003e#\u003c\u003e#\u003c\u003e#########",
|
"####<>#<>#<>#<>#<>#<>#<>#########",
|
||||||
"#..\u003c..\u003ci.\u003cT.#..#..#..\u003c..#..\u003c..###",
|
"#..<..<i.<T.#..#..#..<..#..<..###",
|
||||||
"#..\u003e..\u003e.K\u003e..#..#..#..\u003e..#..\u003e..###",
|
"#..>..>.K>..#..#..#..>..#..>..###",
|
||||||
"#\u003c\u003e#\u003c\u003e#######\u003c\u003e#\u003c\u003e#\u003c\u003e#\u003c\u003e####\u003c\u003e###",
|
"#<>#<>#######<>#<>#<>#<>####<>###",
|
||||||
"#..#..\u003c..\u003c..#..#..\u003c.i#..\u003c..\u003c..###",
|
"#..#..<..<..#..#..<.i#..<..<..###",
|
||||||
"#..#..\u003e..\u003e..#..#..\u003e..#..\u003e..\u003e..###",
|
"#..#..>..>..#..#..>..#..>..>..###",
|
||||||
"####\u003c\u003e####\u003c\u003e#\u003c\u003e#G\u003e##########\u003c\u003e###",
|
"####<>####<>#<>#G>##########<>###",
|
||||||
"#.G\u003cW.\u003c..\u003c..#..#.G#..\u003c..\u003c..\u003c.G###",
|
"#.G<W.<..<..#..#.G#..<..<..<.G###",
|
||||||
"#p.G..\u003e..\u003e..#..#Q.#..\u003e..\u003e..\u003eGP###",
|
"#p.G..>..>..#..#Q.#..>..>..>GP###",
|
||||||
"##W##############################",
|
"##W##############################",
|
||||||
"#################################",
|
"#################################",
|
||||||
"#################################"
|
"#################################"
|
||||||
@@ -36,9 +36,9 @@
|
|||||||
"individualCards": true,
|
"individualCards": true,
|
||||||
"kind": "leg",
|
"kind": "leg",
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Ueno Station",
|
"en": "Ueno",
|
||||||
"ja": "上野駅",
|
"ja": "上野駅",
|
||||||
"sv": "Ueno station"
|
"sv": "Ueno"
|
||||||
},
|
},
|
||||||
"noise": 2,
|
"noise": 2,
|
||||||
"oneWayGates": true,
|
"oneWayGates": true,
|
||||||
@@ -57,9 +57,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": {
|
"text": {
|
||||||
"en": "\u003c- Track 2 Ginza Line for Shibuya Track 1 Ginza Line for Asakusa -\u003e",
|
"en": "<- Track 2 Ginza Line for Shibuya Track 1 Ginza Line for Asakusa ->",
|
||||||
"ja": "← 2番線 銀座線 渋谷方面 1番線 銀座線 浅草方面 →",
|
"ja": "← 2番線 銀座線 渋谷方面 1番線 銀座線 浅草方面 →",
|
||||||
"sv": "\u003c- Spår 2 Ginza-linjen mot Shibuya Spår 1 Ginza-linjen mot Asakusa -\u003e"
|
"sv": "<- Spår 2 Ginza-linjen mot Shibuya Spår 1 Ginza-linjen mot Asakusa ->"
|
||||||
},
|
},
|
||||||
"x": 20,
|
"x": 20,
|
||||||
"y": 13,
|
"y": 13,
|
||||||
|
|||||||
@@ -68,9 +68,9 @@
|
|||||||
"kind": "main",
|
"kind": "main",
|
||||||
"maxMissedTrains": 3,
|
"maxMissedTrains": 3,
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Tokyo Station, going home",
|
"en": "Tokyo",
|
||||||
"ja": "東京駅",
|
"ja": "東京駅",
|
||||||
"sv": "Tokyo station, hemresan"
|
"sv": "Tokyo"
|
||||||
},
|
},
|
||||||
"next": "008b-nippori",
|
"next": "008b-nippori",
|
||||||
"noise": 2,
|
"noise": 2,
|
||||||
|
|||||||
@@ -36,9 +36,9 @@
|
|||||||
"individualCards": true,
|
"individualCards": true,
|
||||||
"kind": "leg",
|
"kind": "leg",
|
||||||
"name": {
|
"name": {
|
||||||
"en": "Nippori Station",
|
"en": "Nippori",
|
||||||
"ja": "日暮里駅",
|
"ja": "日暮里駅",
|
||||||
"sv": "Nippori station"
|
"sv": "Nippori"
|
||||||
},
|
},
|
||||||
"noise": 1,
|
"noise": 1,
|
||||||
"oneWayGates": true,
|
"oneWayGates": true,
|
||||||
@@ -58,9 +58,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": {
|
"text": {
|
||||||
"en": "\u003c- Track 2 Keisei Line for Keisei-Ueno Track 1 Keisei Line for Narita Airport -\u003e",
|
"en": "<- Track 2 Keisei Line for Keisei-Ueno Track 1 Keisei Line for Narita Airport ->",
|
||||||
"ja": "← 2番線 京成本線 京成上野方面 1番線 京成本線 成田空港方面 →",
|
"ja": "← 2番線 京成本線 京成上野方面 1番線 京成本線 成田空港方面 →",
|
||||||
"sv": "\u003c- Spår 2 Keisei-linjen mot Keisei-Ueno Spår 1 Keisei-linjen mot Narita flygplats -\u003e"
|
"sv": "<- Spår 2 Keisei-linjen mot Keisei-Ueno Spår 1 Keisei-linjen mot Narita flygplats ->"
|
||||||
},
|
},
|
||||||
"x": 1,
|
"x": 1,
|
||||||
"y": 17,
|
"y": 17,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"menu.endless": "Endless mode",
|
"menu.endless": "Endless mode",
|
||||||
"menu.settings": "Settings",
|
"menu.settings": "Settings",
|
||||||
"menu.quit": "Quit",
|
"menu.quit": "Quit",
|
||||||
"hud.destination": "Destination",
|
"hud.destination": "To",
|
||||||
"hud.next_train": "Next train",
|
"hud.next_train": "Next train",
|
||||||
"hud.card_balance": "Card balance",
|
"hud.card_balance": "Card balance",
|
||||||
"meter.food": "Food",
|
"meter.food": "Food",
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
"hud.progress": "Progress",
|
"hud.progress": "Progress",
|
||||||
"words.title": "Word list 言葉",
|
"words.title": "Word list 言葉",
|
||||||
"words.points": "Points",
|
"words.points": "Points",
|
||||||
"words.hint": "Arrows: select Enter: learn the word for points Esc/L: back",
|
"words.hint": "Arrows: select Enter: learn for points Esc: back",
|
||||||
"words.already": "You already know that word.",
|
"words.already": "You already know that word.",
|
||||||
"words.bought": "New word: %s = %s",
|
"words.bought": "New word: %s = %s",
|
||||||
"words.too_expensive": "Costs %d points - clear levels with good times for more.",
|
"words.too_expensive": "Costs %d points - clear levels with good times for more.",
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
"mini.ticket.title": "Tickets",
|
"mini.ticket.title": "Tickets",
|
||||||
"mini.ticket.hint": "Buy a ticket to %s. Cash: %d¥",
|
"mini.ticket.hint": "Buy a ticket to %s. Cash: %d¥",
|
||||||
"mini.crowd.title": "Crowded train - squeeze in",
|
"mini.crowd.title": "Crowded train - squeeze in",
|
||||||
"mini.crowd.hint": "Press Enter when the marker is in the green zone (%d tries left)",
|
"mini.crowd.hint": "Enter when the marker is in the green zone (%d left)",
|
||||||
"mini.ride.title": "On the train - next stop",
|
"mini.ride.title": "On the train - next stop",
|
||||||
"mini.ride.hint": "Enter: get off here (you are going to %s)",
|
"mini.ride.hint": "Enter: get off here (you are going to %s)",
|
||||||
"hud.time_left": "Left",
|
"hud.time_left": "Left",
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
"map.party": "Party",
|
"map.party": "Party",
|
||||||
"map.resume": "⟳ resume (saved seed)",
|
"map.resume": "⟳ resume (saved seed)",
|
||||||
"map.locked": "Locked - clear the previous level first.",
|
"map.locked": "Locked - clear the previous level first.",
|
||||||
"map.hint": "Enter start N mode L words T skills I items P phone S set.",
|
"map.hint": "Enter: start level N: mode L: words T: skills",
|
||||||
"skills.title": "Skills 技",
|
"skills.title": "Skills 技",
|
||||||
"skills.to_next": "XP to next",
|
"skills.to_next": "XP to next",
|
||||||
"skills.already": "Already unlocked.",
|
"skills.already": "Already unlocked.",
|
||||||
@@ -185,7 +185,7 @@
|
|||||||
"mini.car.title": "Choose a car",
|
"mini.car.title": "Choose a car",
|
||||||
"mini.car.alighting": "People are getting off - wait…",
|
"mini.car.alighting": "People are getting off - wait…",
|
||||||
"mini.car.pick": "Pick a car with the arrows, board with Enter.",
|
"mini.car.pick": "Pick a car with the arrows, board with Enter.",
|
||||||
"mini.car.men": "There is a man in the party - not the women-only car (pink)!",
|
"mini.car.men": "Man in the party - avoid the women-only car (pink)!",
|
||||||
"mini.car.hint": "←/→: car Enter: board Esc: cancel",
|
"mini.car.hint": "←/→: car Enter: board Esc: cancel",
|
||||||
"car.crowd.low": "few people",
|
"car.crowd.low": "few people",
|
||||||
"car.crowd.mid": "some people",
|
"car.crowd.mid": "some people",
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
"ending.hint": "Enter: continue Esc: level map",
|
"ending.hint": "Enter: continue Esc: level map",
|
||||||
"ending.credits": "Metro Puzzle JP\n\nIdea and game design\nBjörn\n\nCode and pixels\nClaude (Anthropic)\n\nEngine\nEbitengine\n\nFont\nPixelMplus (M+ FONT LICENSE)\n\nThe trains, the signs and the chatter\nTokyo\n\nThanks for riding!\nまたね",
|
"ending.credits": "Metro Puzzle JP\n\nIdea and game design\nBjörn\n\nCode and pixels\nClaude (Anthropic)\n\nEngine\nEbitengine\n\nFont\nPixelMplus (M+ FONT LICENSE)\n\nThe trains, the signs and the chatter\nTokyo\n\nThanks for riding!\nまたね",
|
||||||
"mini.payer.title": "Who pays?",
|
"mini.payer.title": "Who pays?",
|
||||||
"mini.payer.price": "To pay: %d¥ (food and drink for everyone who needs it)",
|
"mini.payer.price": "To pay: %d¥ (for everyone who needs it)",
|
||||||
"mini.payer.cash": "Cash",
|
"mini.payer.cash": "Cash",
|
||||||
"mini.payer.card": "card",
|
"mini.payer.card": "card",
|
||||||
"mini.payer.short": "Not enough - pick someone else.",
|
"mini.payer.short": "Not enough - pick someone else.",
|
||||||
@@ -291,5 +291,6 @@
|
|||||||
"tip.sign": "Tip: reading the sign - {ja} is the destination (highlighted), 番線 = track, 方面 = for. Press H to ask for help.",
|
"tip.sign": "Tip: reading the sign - {ja} is the destination (highlighted), 番線 = track, 方面 = for. Press H to ask for help.",
|
||||||
"tip.party": "Tip: someone in the party needs something. The kiosk (K) gives food and drink, the toilet (W) solves toilet needs.",
|
"tip.party": "Tip: someone in the party needs something. The kiosk (K) gives food and drink, the toilet (W) solves toilet needs.",
|
||||||
"tip.gate": "Tip: the gate charges the fare from your card when you enter. Top up an empty card at the machine (T).",
|
"tip.gate": "Tip: the gate charges the fare from your card when you enter. Top up an empty card at the machine (T).",
|
||||||
"tip.platform": "Tip: wait for the train and press Enter when it is in. Let people off, pick a car with the arrows."
|
"tip.platform": "Tip: wait for the train and press Enter when it is in. Let people off, pick a car with the arrows.",
|
||||||
|
"map.hint2": "I: wardrobe P: phone S: settings Esc: title"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"menu.endless": "Oändligt läge",
|
"menu.endless": "Oändligt läge",
|
||||||
"menu.settings": "Inställningar",
|
"menu.settings": "Inställningar",
|
||||||
"menu.quit": "Avsluta",
|
"menu.quit": "Avsluta",
|
||||||
"hud.destination": "Resmål",
|
"hud.destination": "Mål",
|
||||||
"hud.next_train": "Nästa tåg",
|
"hud.next_train": "Nästa tåg",
|
||||||
"hud.card_balance": "Kortsaldo",
|
"hud.card_balance": "Kortsaldo",
|
||||||
"meter.food": "Mat",
|
"meter.food": "Mat",
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
"hud.progress": "Framsteg",
|
"hud.progress": "Framsteg",
|
||||||
"words.title": "Ordlista 言葉",
|
"words.title": "Ordlista 言葉",
|
||||||
"words.points": "Poäng",
|
"words.points": "Poäng",
|
||||||
"words.hint": "Pilar: välj Enter: lär dig ordet för poäng Esc/L: tillbaka",
|
"words.hint": "Pilar: välj Enter: lär ordet för poäng Esc: tillbaka",
|
||||||
"words.already": "Det ordet kan du redan.",
|
"words.already": "Det ordet kan du redan.",
|
||||||
"words.bought": "Nytt ord: %s = %s",
|
"words.bought": "Nytt ord: %s = %s",
|
||||||
"words.too_expensive": "Kostar %d poäng - klara banor med bra tid för fler.",
|
"words.too_expensive": "Kostar %d poäng - klara banor med bra tid för fler.",
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
"mini.ticket.title": "Biljetter",
|
"mini.ticket.title": "Biljetter",
|
||||||
"mini.ticket.hint": "Köp biljett till %s. Kontant: %d¥",
|
"mini.ticket.hint": "Köp biljett till %s. Kontant: %d¥",
|
||||||
"mini.crowd.title": "Fullt tåg - tränga sig på",
|
"mini.crowd.title": "Fullt tåg - tränga sig på",
|
||||||
"mini.crowd.hint": "Tryck Enter när markören är i det gröna fältet (%d försök kvar)",
|
"mini.crowd.hint": "Enter när markören är i det gröna fältet (%d försök kvar)",
|
||||||
"mini.ride.title": "På tåget - nästa station",
|
"mini.ride.title": "På tåget - nästa station",
|
||||||
"mini.ride.hint": "Enter: kliv av här (du ska till %s)",
|
"mini.ride.hint": "Enter: kliv av här (du ska till %s)",
|
||||||
"hud.time_left": "Kvar",
|
"hud.time_left": "Kvar",
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
"map.party": "Sällskap",
|
"map.party": "Sällskap",
|
||||||
"map.resume": "⟳ fortsätt (sparad seed)",
|
"map.resume": "⟳ fortsätt (sparad seed)",
|
||||||
"map.locked": "Låst - klara föregående bana först.",
|
"map.locked": "Låst - klara föregående bana först.",
|
||||||
"map.hint": "Enter start N läge L ord T skills I items P tel S inst.",
|
"map.hint": "Enter: starta banan N: läge L: ordlista T: skills",
|
||||||
"skills.title": "Skills 技",
|
"skills.title": "Skills 技",
|
||||||
"skills.to_next": "XP till nästa",
|
"skills.to_next": "XP till nästa",
|
||||||
"skills.already": "Redan upplåst.",
|
"skills.already": "Redan upplåst.",
|
||||||
@@ -185,7 +185,7 @@
|
|||||||
"mini.car.title": "Välj vagn",
|
"mini.car.title": "Välj vagn",
|
||||||
"mini.car.alighting": "Folk kliver av - vänta…",
|
"mini.car.alighting": "Folk kliver av - vänta…",
|
||||||
"mini.car.pick": "Välj vagn med pilarna, kliv på med Enter.",
|
"mini.car.pick": "Välj vagn med pilarna, kliv på med Enter.",
|
||||||
"mini.car.men": "Det finns en man i sällskapet - inte kvinnovagnen (rosa)!",
|
"mini.car.men": "Man i sällskapet - undvik kvinnovagnen (rosa)!",
|
||||||
"mini.car.hint": "←/→: vagn Enter: kliv på Esc: avbryt",
|
"mini.car.hint": "←/→: vagn Enter: kliv på Esc: avbryt",
|
||||||
"car.crowd.low": "lite folk",
|
"car.crowd.low": "lite folk",
|
||||||
"car.crowd.mid": "en del folk",
|
"car.crowd.mid": "en del folk",
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
"ending.hint": "Enter: vidare Esc: bankartan",
|
"ending.hint": "Enter: vidare Esc: bankartan",
|
||||||
"ending.credits": "Metro Puzzle JP\n\nIdé och speldesign\nBjörn\n\nKod och pixlar\nClaude (Anthropic)\n\nMotor\nEbitengine\n\nTypsnitt\nPixelMplus (M+ FONT LICENSE)\n\nTågen, skyltarna och sorlet\nTokyo\n\nTack för resan!\nまたね",
|
"ending.credits": "Metro Puzzle JP\n\nIdé och speldesign\nBjörn\n\nKod och pixlar\nClaude (Anthropic)\n\nMotor\nEbitengine\n\nTypsnitt\nPixelMplus (M+ FONT LICENSE)\n\nTågen, skyltarna och sorlet\nTokyo\n\nTack för resan!\nまたね",
|
||||||
"mini.payer.title": "Vem betalar?",
|
"mini.payer.title": "Vem betalar?",
|
||||||
"mini.payer.price": "Att betala: %d¥ (mat och dryck åt alla som behöver)",
|
"mini.payer.price": "Att betala: %d¥ (åt alla som behöver)",
|
||||||
"mini.payer.cash": "Kontant",
|
"mini.payer.cash": "Kontant",
|
||||||
"mini.payer.card": "kort",
|
"mini.payer.card": "kort",
|
||||||
"mini.payer.short": "Det räcker inte - välj någon annan.",
|
"mini.payer.short": "Det räcker inte - välj någon annan.",
|
||||||
@@ -243,7 +243,7 @@
|
|||||||
"result.endless_won": "Mål %d klart! Totalt %d poäng.",
|
"result.endless_won": "Mål %d klart! Totalt %d poäng.",
|
||||||
"result.endless_lost": "Oändligt läge slut efter %d mål: %d poäng (bästa %d).",
|
"result.endless_lost": "Oändligt läge slut efter %d mål: %d poäng (bästa %d).",
|
||||||
"result.endless_best": "Ny bästa!",
|
"result.endless_best": "Ny bästa!",
|
||||||
"result.hint_endless": "Enter: nästa mål L: ordlista Esc: avsluta omgången",
|
"result.hint_endless": "Enter: nästa mål L: ord Esc: avsluta",
|
||||||
"result.hint_endless_over": "Enter/Esc: bankartan L: ordlista",
|
"result.hint_endless_over": "Enter/Esc: bankartan L: ordlista",
|
||||||
"settings.title": "Inställningar",
|
"settings.title": "Inställningar",
|
||||||
"settings.master": "Huvudvolym",
|
"settings.master": "Huvudvolym",
|
||||||
@@ -266,7 +266,7 @@
|
|||||||
"pause.leave": "Lämna banan (framsteg på banan försvinner)",
|
"pause.leave": "Lämna banan (framsteg på banan försvinner)",
|
||||||
"pause.quit": "Avsluta spelet",
|
"pause.quit": "Avsluta spelet",
|
||||||
"pause.hint": "Esc: fortsätt Enter: välj",
|
"pause.hint": "Esc: fortsätt Enter: välj",
|
||||||
"pause.quit_confirm": "Enter igen för att avsluta spelet, Esc avbryter.",
|
"pause.quit_confirm": "Enter igen avslutar spelet, Esc avbryter.",
|
||||||
"tutorial.hint": "←/→ eller Enter: bläddra Esc: tillbaka",
|
"tutorial.hint": "←/→ eller Enter: bläddra Esc: tillbaka",
|
||||||
"tutorial.page": "sida %d/%d",
|
"tutorial.page": "sida %d/%d",
|
||||||
"tutorial.p1.title": "Målet och stationen",
|
"tutorial.p1.title": "Målet och stationen",
|
||||||
@@ -291,5 +291,6 @@
|
|||||||
"tip.sign": "Tips: så läser du skylten - {ja} är resmålet (markerat), 番線 = spår, 方面 = mot. Tryck H för att fråga om hjälp.",
|
"tip.sign": "Tips: så läser du skylten - {ja} är resmålet (markerat), 番線 = spår, 方面 = mot. Tryck H för att fråga om hjälp.",
|
||||||
"tip.party": "Tips: någon i sällskapet behöver något. Kiosken (K) ger mat och dryck, toaletten (W) löser toabehov.",
|
"tip.party": "Tips: någon i sällskapet behöver något. Kiosken (K) ger mat och dryck, toaletten (W) löser toabehov.",
|
||||||
"tip.gate": "Tips: spärren drar avgiften från kortet när du går in. Tomt kort fylls på i automaten (T).",
|
"tip.gate": "Tips: spärren drar avgiften från kortet när du går in. Tomt kort fylls på i automaten (T).",
|
||||||
"tip.platform": "Tips: vänta in tåget och tryck Enter när det står inne. Låt folk kliva av, välj vagn med pilarna."
|
"tip.platform": "Tips: vänta in tåget och tryck Enter när det står inne. Låt folk kliva av, välj vagn med pilarna.",
|
||||||
|
"map.hint2": "I: garderob P: telefon S: inställningar Esc: titel"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,19 @@ import (
|
|||||||
|
|
||||||
// demoSave skriver en sparfil med lite progression så att kartan, skills
|
// demoSave skriver en sparfil med lite progression så att kartan, skills
|
||||||
// och garderoben har något att visa. Används bara av skärmdumparna.
|
// och garderoben har något att visa. Används bara av skärmdumparna.
|
||||||
|
// demoSaveEN är samma demosparfil på engelska (för språkkontrollen).
|
||||||
|
func demoSaveEN(path string) error {
|
||||||
|
if err := demoSave(path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
st, _, err := save.Load(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
st.Settings.Lang = "en"
|
||||||
|
return save.Store(path, st)
|
||||||
|
}
|
||||||
|
|
||||||
func demoSave(path string) error {
|
func demoSave(path string) error {
|
||||||
st := save.New()
|
st := save.New()
|
||||||
st.Know.Learn("改札")
|
st.Know.Learn("改札")
|
||||||
@@ -55,7 +68,7 @@ type shot struct {
|
|||||||
// bilderna överlever ändringar i banorna.
|
// bilderna överlever ändringar i banorna.
|
||||||
var shots []shot
|
var shots []shot
|
||||||
|
|
||||||
var demo, fresh string
|
var demo, demoEN, fresh string
|
||||||
|
|
||||||
func buildShots(levelsDir string) error {
|
func buildShots(levelsDir string) error {
|
||||||
l, err := level.LoadFile(filepath.Join(levelsDir, "001-flygplatsen.json"))
|
l, err := level.LoadFile(filepath.Join(levelsDir, "001-flygplatsen.json"))
|
||||||
@@ -180,6 +193,8 @@ func buildShots(levelsDir string) error {
|
|||||||
{"", "wait 150; shot $OUT/ending.png; press enter; wait 5; shot $OUT/ending-summary.png; press enter; wait 250; shot $OUT/credits.png; quit", []string{"-ending", "-save", "$DEMO"}},
|
{"", "wait 150; shot $OUT/ending.png; press enter; wait 5; shot $OUT/ending-summary.png; press enter; wait 250; shot $OUT/credits.png; quit", []string{"-ending", "-save", "$DEMO"}},
|
||||||
{"007-akihabara", toKiosk7 + "; urge Ville; press enter; wait 5; press right; wait 3; shot $OUT/payer.png; quit", []string{"-seed", "7"}},
|
{"007-akihabara", toKiosk7 + "; urge Ville; press enter; wait 5; press right; wait 3; shot $OUT/payer.png; quit", []string{"-seed", "7"}},
|
||||||
{"", "wait 10; shot $OUT/endless.png; quit", []string{"-endless", "-seed", "7", "-mode", "list", "-save", "$DEMO"}},
|
{"", "wait 10; shot $OUT/endless.png; quit", []string{"-endless", "-seed", "7", "-mode", "list", "-save", "$DEMO"}},
|
||||||
|
{"", "wait 5; press enter; wait 10; shot $OUT/map-en.png; quit", []string{"-save", "$DEMOEN"}},
|
||||||
|
{"001-flygplatsen", toSign + "; wait 5; shot $OUT/sign-en.png; quit", []string{"-seed", "7", "-save", "$DEMOEN", "-know", "改札,番線,方面"}},
|
||||||
{"001b-flygbussen", toMachine1b + "; press enter; pick 新宿; press enter; wait 5; " + toStop1b + "; waittrain; wait 10; shot $OUT/bus.png; quit", []string{"-seed", "7", "-know", "新宿,方面"}},
|
{"001b-flygbussen", toMachine1b + "; press enter; pick 新宿; press enter; wait 5; " + toStop1b + "; waittrain; wait 10; shot $OUT/bus.png; quit", []string{"-seed", "7", "-know", "新宿,方面"}},
|
||||||
{"", "wait 5; press enter; wait 10; press s; wait 5; press down; wait 3; shot $OUT/settings.png; quit", []string{"-save", "$DEMO"}},
|
{"", "wait 5; press enter; wait 10; press s; wait 5; press down; wait 3; shot $OUT/settings.png; quit", []string{"-save", "$DEMO"}},
|
||||||
{"", "wait 10; shot $OUT/lang.png; press enter; wait 30; shot $OUT/title.png; press down; press enter; wait 5; press right; wait 5; shot $OUT/tutorial.png; quit", []string{"-save", "$FRESH"}},
|
{"", "wait 10; shot $OUT/lang.png; press enter; wait 30; shot $OUT/title.png; press down; press enter; wait 5; press right; wait 5; shot $OUT/tutorial.png; quit", []string{"-save", "$FRESH"}},
|
||||||
@@ -269,6 +284,8 @@ func main() {
|
|||||||
must(demoSave(demo))
|
must(demoSave(demo))
|
||||||
fresh = filepath.Join(root, "build", "fresh-save.json") // finns inte: första starten (språkval, titel, tips)
|
fresh = filepath.Join(root, "build", "fresh-save.json") // finns inte: första starten (språkval, titel, tips)
|
||||||
os.Remove(fresh)
|
os.Remove(fresh)
|
||||||
|
demoEN = filepath.Join(root, "build", "demo-save-en.json")
|
||||||
|
must(demoSaveEN(demoEN))
|
||||||
must(buildShots(filepath.Join(root, "resources", "data", "levels")))
|
must(buildShots(filepath.Join(root, "resources", "data", "levels")))
|
||||||
|
|
||||||
build := exec.Command("go", "run", "./tools/build", "-mode", "debug")
|
build := exec.Command("go", "run", "./tools/build", "-mode", "debug")
|
||||||
@@ -297,7 +314,7 @@ func main() {
|
|||||||
args = append(args, "-level", s.level)
|
args = append(args, "-level", s.level)
|
||||||
}
|
}
|
||||||
for _, a := range s.args {
|
for _, a := range s.args {
|
||||||
args = append(args, strings.ReplaceAll(strings.ReplaceAll(a, "$DEMO", demo), "$FRESH", fresh))
|
args = append(args, strings.NewReplacer("$DEMOEN", demoEN, "$DEMO", demo, "$FRESH", fresh).Replace(a))
|
||||||
}
|
}
|
||||||
run := exec.Command(exe, args...)
|
run := exec.Command(exe, args...)
|
||||||
run.Dir, run.Stdout, run.Stderr = filepath.Dir(exe), os.Stdout, os.Stderr
|
run.Dir, run.Stdout, run.Stderr = filepath.Dir(exe), os.Stdout, os.Stderr
|
||||||
|
|||||||