Files
metro-puzzle-jp/internal/game/audio.go
claude de61568e44
All checks were successful
release / build-release (push) Successful in 1m33s
M9b: folk, filer, reklam, sorl och vagnval
Folk på stationen (NPC:er som vandrar, kliver av tåg och går mot
utgången, blockerar spelaren men inte varandra), vänster-/högerfiler
som bromsar på fel sida, bredare gångar, reklamaffischer (clutter),
japanskt sorl i loop (noise), vagnval med kvinnovagn (stort avdrag med
man i sällskapet, tidsstyrd), kömarkeringar på perrongen, trängsel per
vagn (bonus för tom vagn, svettig-debuff och svårare resa i full vagn,
släpp förbi passagerare) och att folk kliver av innan man får gå på.
Ny bana 005 Ueno. Skärmdumpsverktyget kan filtrera på bana/skript.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 12:33:44 +02:00

104 lines
2.3 KiB
Go
Raw 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 game
import (
"bytes"
"fmt"
"io"
"math"
"github.com/hajimehoshi/ebiten/v2/audio"
"github.com/hajimehoshi/ebiten/v2/audio/wav"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/res"
)
// Audio spelar korta effekter. nil-säkert: utan ljud (−mute eller fel) är
// alla anrop no-ops.
type Audio struct {
ctx *audio.Context
clips map[string][]byte // avkodad PCM (16-bit stereo LE) per klipp
loop *audio.Player // pågående bakgrundsloop (bakgrundsprat)
}
const sampleRate = 44100
// Ljudklipp (filnamn utan .wav i resources/sound).
const (
SoundApproach = "melody-approach"
SoundDoors = "door-chime"
SoundChatter = "chatter"
)
// AudibleTiles är hur många rutor bort man hör perrongens ljud.
const AudibleTiles = 12.0
// newAudio laddar klippen. Returnerar nil om ljudet är avstängt.
func newAudio(mute bool) (*Audio, error) {
if mute {
return nil, nil
}
a := &Audio{ctx: audio.NewContext(sampleRate), clips: map[string][]byte{}}
for _, name := range []string{SoundApproach, SoundDoors, SoundChatter} {
b, err := res.ReadFile("sound", name+".wav")
if err != nil {
return nil, err
}
stream, err := wav.DecodeWithoutResampling(bytes.NewReader(b))
if err != nil {
return nil, fmt.Errorf("ljud %s: %w", name, err)
}
pcm, err := io.ReadAll(stream)
if err != nil {
return nil, err
}
a.clips[name] = pcm
}
return a, nil
}
// Play spelar ett klipp med volym 0..1.
func (a *Audio) Play(name string, volume float64) {
if a == nil || volume <= 0 {
return
}
pcm, ok := a.clips[name]
if !ok {
return
}
p := a.ctx.NewPlayerFromBytes(pcm)
p.SetVolume(math.Min(1, volume))
p.Play()
}
// Loop startar (eller byter) bakgrundsloopen med given volym; 0 stoppar.
func (a *Audio) Loop(name string, volume float64) {
if a == nil {
return
}
a.StopLoop()
pcm, ok := a.clips[name]
if !ok || volume <= 0 {
return
}
stream := audio.NewInfiniteLoop(bytes.NewReader(pcm), int64(len(pcm)))
p, err := a.ctx.NewPlayer(stream)
if err != nil {
return
}
p.SetVolume(math.Min(1, volume))
p.Play()
a.loop = p
}
// StopLoop stoppar bakgrundsloopen.
func (a *Audio) StopLoop() {
if a == nil || a.loop == nil {
return
}
a.loop.Close()
a.loop = nil
}
// volumeAt ger volym efter avstånd i rutor.
func volumeAt(dist float64) float64 { return math.Max(0, 1-dist/AudibleTiles) }