M9b: folk, filer, reklam, sorl och vagnval
All checks were successful
release / build-release (push) Successful in 1m33s
All checks were successful
release / build-release (push) Successful in 1m33s
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>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// Skriver resources/sprites/tiles.png (en remsa 16×16-rutor i ordningen
|
||||
// vägg, golv, start, perrong, spärr, automat, skylt, tågvagn, toalett, kiosk, räls,
|
||||
// trappa upp, trappa ner) och
|
||||
// trappa upp, trappa ner, fil vänster, fil höger, affisch ×3) och
|
||||
// resources/sprites/player.png (3 bildrutor × 4 riktningar: ner, upp,
|
||||
// vänster, höger; kolumn 0 = stilla, 1–2 = gå). Ritas med enkla rektanglar
|
||||
// så att allt är deterministiskt och lätt att justera.
|
||||
@@ -163,6 +163,44 @@ var tiles = []func(canvas){
|
||||
c.rect(6, 13, 4, 1, boxDark)
|
||||
c.rect(5, 12, 6, 1, boxDark)
|
||||
},
|
||||
func(c canvas) { // fil vänster: golv med vänsterpilar
|
||||
floor(c)
|
||||
for _, y := range []int{3, 11} {
|
||||
c.rect(4, y, 8, 1, metalDark)
|
||||
c.rect(3, y-1, 1, 3, metalDark)
|
||||
}
|
||||
},
|
||||
func(c canvas) { // fil höger: golv med högerpilar
|
||||
floor(c)
|
||||
for _, y := range []int{3, 11} {
|
||||
c.rect(4, y, 8, 1, metalDark)
|
||||
c.rect(12, y-1, 1, 3, metalDark)
|
||||
}
|
||||
},
|
||||
func(c canvas) { // affisch 1: färgglad reklam
|
||||
c.rect(0, 0, 16, 16, wallMid)
|
||||
c.rect(2, 1, 12, 14, rgb(250, 230, 120))
|
||||
c.rect(3, 2, 10, 5, rgb(228, 59, 68))
|
||||
c.rect(4, 8, 8, 1, boxDark)
|
||||
c.rect(4, 10, 6, 1, boxDark)
|
||||
c.rect(4, 12, 7, 1, boxDark)
|
||||
},
|
||||
func(c canvas) { // affisch 2: blå lockskylt som liknar en riktig skylt
|
||||
c.rect(0, 0, 16, 16, wallMid)
|
||||
c.rect(1, 2, 14, 12, signBoard)
|
||||
c.rect(1, 2, 14, 1, white)
|
||||
c.rect(3, 5, 10, 1, white)
|
||||
c.rect(3, 8, 6, 1, white)
|
||||
c.rect(3, 11, 8, 1, yellow)
|
||||
},
|
||||
func(c canvas) { // affisch 3: rosa med stort tecken
|
||||
c.rect(0, 0, 16, 16, wallMid)
|
||||
c.rect(2, 1, 12, 14, rgb(255, 170, 200))
|
||||
c.rect(5, 4, 6, 6, white)
|
||||
c.rect(7, 4, 2, 6, rgb(200, 60, 120))
|
||||
c.rect(5, 6, 6, 2, rgb(200, 60, 120))
|
||||
c.rect(4, 12, 8, 1, rgb(120, 30, 70))
|
||||
},
|
||||
func(c canvas) { // kiosk: randig markis och disk
|
||||
floor(c)
|
||||
c.rect(1, 1, 14, 3, awningRed)
|
||||
|
||||
@@ -83,12 +83,39 @@ func writeWAV(path string, pcm []int16) error {
|
||||
return binary.Write(f, binary.LittleEndian, pcm)
|
||||
}
|
||||
|
||||
// chatter syntetiserar sex sekunder loopbart stationssorl: lågpassat brus
|
||||
// med stavelseliknande amplitudmodulation från en deterministisk LCG.
|
||||
func chatter(seconds float64) []int16 {
|
||||
n := int(seconds * rate)
|
||||
out := make([]int16, n)
|
||||
var lcg uint32 = 12345
|
||||
next := func() float64 { lcg = lcg*1664525 + 1013904223; return float64(lcg>>8)/float64(1<<24)*2 - 1 }
|
||||
lp := 0.0
|
||||
env, envTarget, envT := 0.3, 0.3, 0.0
|
||||
for i := 0; i < n; i++ {
|
||||
t := float64(i) / rate
|
||||
// ny "stavelse" var 120–250 ms
|
||||
if t >= envT {
|
||||
envTarget = 0.1 + 0.6*math.Abs(next())
|
||||
envT = t + 0.12 + 0.13*math.Abs(next())
|
||||
}
|
||||
env += (envTarget - env) * 0.002
|
||||
lp += (next() - lp) * 0.08 // lågpass ≈ 600 Hz
|
||||
v := lp * env * 0.6
|
||||
// mjuk in-/utfasning för sömlös loop
|
||||
fade := math.Min(1, math.Min(t, seconds-t)/0.2)
|
||||
out[i] = int16(v * fade * 32767)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func main() {
|
||||
out := filepath.Join(repoRoot(), "resources", "sound")
|
||||
must(os.MkdirAll(out, 0o755))
|
||||
must(writeWAV(filepath.Join(out, "melody-approach.wav"), render(approachMelody, 0.01, 0.06, 0.3)))
|
||||
must(writeWAV(filepath.Join(out, "door-chime.wav"), render(doorChime, 0.005, 0.04, 0.1)))
|
||||
fmt.Println("skrev", filepath.Join(out, "melody-approach.wav"), "och door-chime.wav")
|
||||
must(writeWAV(filepath.Join(out, "chatter.wav"), chatter(6)))
|
||||
fmt.Println("skrev", filepath.Join(out, "melody-approach.wav"), ", door-chime.wav och chatter.wav")
|
||||
}
|
||||
|
||||
func repoRoot() string {
|
||||
|
||||
@@ -102,6 +102,14 @@ func buildShots(levelsDir string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l5, err := level.LoadFile(filepath.Join(levelsDir, "005-ueno.json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
toPlatform5, err := moves(l5, l5.SpawnPoint(), l5.CorrectPlatform())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Skyltbilderna tas med några kända ord så att markeringen och den
|
||||
// delvisa översättningen syns; hjälpen lär sedan in nästa ord.
|
||||
known := []string{"-know", "改札,番線,方面", "-seed", "7"}
|
||||
@@ -118,6 +126,7 @@ func buildShots(levelsDir string) error {
|
||||
{"003-shibuya", toMachine3 + "; press enter; pick 池袋; press enter; wait 5; press enter; press right; wait 5; shot $OUT/seats.png; quit",
|
||||
[]string{"-seed", "7", "-know", "池袋", "-debuffs", "sore_feet"}},
|
||||
{"004-tokyo", "wait 10; shot $OUT/floors-b1.png; " + toStairs + "; wait 20; shot $OUT/floors-1f.png; quit", []string{"-seed", "7"}},
|
||||
{"005-ueno", "wait 90; shot $OUT/crowd.png; " + toPlatform5 + "; waittrain; wait 5; press enter; wait 20; shot $OUT/cars-alight.png; wait 80; press right; wait 5; shot $OUT/cars.png; quit", []string{"-seed", "7"}},
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -190,6 +199,9 @@ func main() {
|
||||
}
|
||||
_ = demo
|
||||
for _, s := range shots {
|
||||
if len(os.Args) > 1 && !matchesAny(s, os.Args[1:]) {
|
||||
continue // `go run ./tools/shots 005 ride` kör bara skott vars namn eller bana innehåller något av orden
|
||||
}
|
||||
args := []string{"-mute", "-nosave", "-script", strings.ReplaceAll(s.script, "$OUT", out)}
|
||||
if s.level != "" {
|
||||
args = append(args, "-level", s.level)
|
||||
@@ -234,3 +246,13 @@ func fail(err error) {
|
||||
fmt.Fprintln(os.Stderr, "shots:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// matchesAny rapporterar om skottets bana eller skript innehåller något av orden.
|
||||
func matchesAny(s shot, words []string) bool {
|
||||
for _, w := range words {
|
||||
if strings.Contains(s.level, w) || strings.Contains(s.script, w) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user