All checks were successful
release / build-release (push) Successful in 1m41s
- sim.Wallet: IC-kort, kontanter, pappersbiljett, telefon med batteri, spenderat; följer med mellan banor (Game.Wallet) - internal/minigame: gemensamt Game-gränssnitt (Update/Done/Success), all slump från del-seeds: TopUp (hitta チャージ, belopp, mata sedlar), Ticket (taxetavla med resmål på japanska), Crowd (tidsfönster, två försök), Ride (stationerna passerar, kliv av vid resmålet) - sim.Run: spärren kräver biljett på biljettbanor annars användbart kort; telefonen ger ×1,1 fart men 15 % seedad omskanning (frysning 1,5 s), dött batteri gör kortet oanvändbart; automaten startar påfyllning eller biljettköp; tåget kontrollerar biljetten; fulla tåg kräver trängsel; resan avgör vinst/förlust (fel station, åkte förbi, fel tåg); StepMini låter klockan och sällskapet ticka medan rörelsen pausar - level: ticketRequired, rideStops, Train.Crowded; 002 är biljettbana med fullt tåg och fyra stopp; ordboken har stationer (kind) + sju nya namn - game: vyer för minispelen, plånbok i HUD, P i menyn = kort i telefonen, förlustorsaker och spenderat på resultatet, flaggor -phone/-card, skriptkommandon hold/waitstop; inforuta/minikarta döljs under minispel - plan: M7b (Björn) – tågvind, ankomstindikator nära rälsen, in-/utkörningsanimation för tåg och bussar – i spelbeskrivning, roadmap, testplan - docs/README/wiki med tre nya bilder (automat, biljettavla, tågresa) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
108 lines
2.5 KiB
Go
108 lines
2.5 KiB
Go
package minigame
|
|
|
|
import "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/rng"
|
|
|
|
// TopUp är påfyllningsautomaten: hitta チャージ-knappen bland japanska
|
|
// knappar i seedad ordning, välj belopp, mata in sedlar genom att hålla
|
|
// Enter. Fel knapp kostar en straffsekund eller två.
|
|
type TopUp struct {
|
|
base
|
|
Stage TopUpStage
|
|
Buttons []string // knapparnas text, seedad ordning
|
|
Cursor int
|
|
Amounts []int
|
|
Mistakes int
|
|
Penalty float64 // sekunder kvar av frysning efter fel knapp
|
|
Fill float64 // 0..1 sedelinmatning
|
|
Amount int // valt belopp (resultat)
|
|
MaxCash int // vad spelaren har råd med
|
|
}
|
|
|
|
// TopUpStage är var i flödet man är.
|
|
type TopUpStage int
|
|
|
|
// Steg.
|
|
const (
|
|
TopUpPickButton TopUpStage = iota
|
|
TopUpPickAmount
|
|
TopUpFeed
|
|
)
|
|
|
|
// Knapparna på en japansk biljettautomat. チャージ är den rätta.
|
|
var topUpButtons = []string{"チャージ", "きっぷ", "定期券", "取消"}
|
|
|
|
// TopUpAmounts är beloppen man kan välja.
|
|
var TopUpAmounts = []int{1000, 2000, 3000, 5000}
|
|
|
|
const (
|
|
topUpPenaltySec = 1.5
|
|
topUpFeedPerSec = 0.8 // hålla Enter: fyller på 1/0.8 s
|
|
topUpDrainPerSec = 1.2
|
|
)
|
|
|
|
// NewTopUp skapar automaten. maxCash begränsar beloppen.
|
|
func NewTopUp(seed rng.Seed, maxCash int) *TopUp {
|
|
r := newRand(seed, "topup")
|
|
g := &TopUp{Buttons: append([]string(nil), topUpButtons...), MaxCash: maxCash}
|
|
r.shuffle(len(g.Buttons), func(i, j int) { g.Buttons[i], g.Buttons[j] = g.Buttons[j], g.Buttons[i] })
|
|
for _, a := range TopUpAmounts {
|
|
if a <= maxCash {
|
|
g.Amounts = append(g.Amounts, a)
|
|
}
|
|
}
|
|
if len(g.Amounts) == 0 {
|
|
g.done = true // inga pengar: inget att göra
|
|
}
|
|
return g
|
|
}
|
|
|
|
// Update för automaten framåt.
|
|
func (g *TopUp) Update(in Input, dt float64) {
|
|
if g.done {
|
|
return
|
|
}
|
|
if in.Cancel {
|
|
g.done = true
|
|
return
|
|
}
|
|
if g.Penalty > 0 {
|
|
g.Penalty -= dt
|
|
return
|
|
}
|
|
switch g.Stage {
|
|
case TopUpPickButton:
|
|
g.Cursor = wrap(g.Cursor+in.Move, len(g.Buttons))
|
|
if in.Confirm {
|
|
if g.Buttons[g.Cursor] == topUpButtons[0] {
|
|
g.Stage, g.Cursor = TopUpPickAmount, 0
|
|
} else {
|
|
g.Mistakes++
|
|
g.Penalty = topUpPenaltySec
|
|
}
|
|
}
|
|
case TopUpPickAmount:
|
|
g.Cursor = wrap(g.Cursor+in.Move, len(g.Amounts))
|
|
if in.Confirm {
|
|
g.Amount = g.Amounts[g.Cursor]
|
|
g.Stage = TopUpFeed
|
|
}
|
|
case TopUpFeed:
|
|
if in.Hold {
|
|
g.Fill += topUpFeedPerSec * dt
|
|
} else {
|
|
g.Fill = max(0, g.Fill-topUpDrainPerSec*dt)
|
|
}
|
|
if g.Fill >= 1 {
|
|
g.Fill = 1
|
|
g.done, g.success = true, true
|
|
}
|
|
}
|
|
}
|
|
|
|
func wrap(i, n int) int {
|
|
if n == 0 {
|
|
return 0
|
|
}
|
|
return ((i % n) + n) % n
|
|
}
|