M12: ljud, inställningar och release v0.1.0
All checks were successful
release / build-release (push) Successful in 1m35s

tools/gensound syntetiserar 21 ljud: fyra stationsmelodier i klockklang
(en per station via melody-fältet eller banans id), ankomstjingel,
ding-dong vid inrullning, dörrsignal vid avgång, spärrens pip och
nekande, kiosk, toalett, lärt ord, bekräfta/varning, byte, personal,
vinst, förlust, flygplan, sorl och menymusik. sfx.go spelar effekter
utifrån vad som hänt i sim.Run sedan förra bildrutan. Ljudlagret har
huvudvolym × effekter/musik och byter inte slinga i onödan.
Inställningar (S på bankartan, sparas): volymer, språk sv/en som laddas
om direkt, fönsterskala, fullskärm (F11 överallt) och tangentöversikt.
Fönsterikon från genart. CHANGELOG.md. Tester för ljudfiler, melodival
och inställningar. Docs, wiki och skärmdumpar uppdaterade.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 08:31:38 +01:00
parent 36a45563ec
commit 86f8130735
47 changed files with 800 additions and 107 deletions

View File

@@ -10,6 +10,7 @@ import (
"time"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/endless"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/lang"
@@ -52,6 +53,7 @@ type Game struct {
Endless *EndlessRun // pågående omgång i oändligt läge (nil = ingen)
savePath string // "" = spara inte
flagScale int // fönsterskala från flaggan (när inställningarna säger 0)
fixedSeed uint64 // >0: alla banor startar med denna seed (tester/skärmdumpar)
scene Scene
next Scene
@@ -76,6 +78,7 @@ type Options struct {
NoSave bool // läs sparfilen men skriv aldrig
Ending bool // öppna slutscenen direkt (tester/skärmdumpar)
Endless bool // starta oändligt läge direkt (tester/skärmdumpar)
Scale int // fönsterskala från flaggan (när inställningarna säger 0)
Mode string // navigeringsläge från start: "list" eller "signs" (tomt = sparfilens)
}
@@ -121,6 +124,8 @@ func New(opt Options) (*Game, error) {
if opt.Mode != "" {
g.Mode = nav.ParseMode(opt.Mode)
}
g.flagScale = max(1, opt.Scale)
g.applySettings()
if !opt.NoSave {
g.savePath = path
}
@@ -278,10 +283,12 @@ func (g *Game) Save() {
// SetScene byter scen från och med nästa uppdatering. Bakgrundsprat hör
// till banan och tystnar när man lämnar den.
func (g *Game) SetScene(s Scene) {
if _, inLevel := s.(*LevelScene); !inLevel {
if _, isResult := s.(*ResultScene); !isResult {
g.Audio.StopLoop()
}
switch s.(type) {
case *LevelScene, *ResultScene: // banans sorl fortsätter
case *MapScene, *SettingsScene, *EndingScene, *WordsScene, *SkillsScene, *ItemsScene:
g.Audio.Loop(SoundMusicMap, 0.6) // menymusiken (fortsätter om den redan spelas)
default:
g.Audio.StopLoop()
}
g.next = s
}
@@ -305,6 +312,11 @@ func (g *Game) Update() error {
if g.next != nil {
g.scene, g.next = g.next, nil
}
if inpututil.IsKeyJustPressed(ebiten.KeyF11) { // fullskärm av/på överallt
g.State.Settings.Fullscreen = !g.State.Settings.Fullscreen
g.applySettings()
g.Save()
}
if g.script != nil {
if err := g.script.tick(g, g.frame); err != nil {
return err
@@ -349,6 +361,25 @@ func (g *Game) runOptions(id string, seed rng.Seed) sim.RunOptions {
}
}
// applySettings för ut inställningarna: volymer, språk, fönster.
func (g *Game) applySettings() {
st := g.State.Settings
g.Audio.SetVolumes(float64(st.Master)/10, float64(st.Effects)/10, float64(st.Music)/10)
if st.Lang != UILang {
if err := g.Assets.LoadLang(st.Lang); err != nil {
log.Printf("språk %s: %v", st.Lang, err)
}
}
scale := st.Scale
if scale <= 0 {
scale = g.flagScale
}
if scale > 0 {
ebiten.SetWindowSize(ScreenW*scale, ScreenH*scale)
}
ebiten.SetFullscreen(st.Fullscreen)
}
// toggleMode växlar navigeringsläge och sparar.
func (g *Game) toggleMode() {
if g.Mode == nav.List {