Files
metro-puzzle-jp/internal/game/input.go
claude 915c684dc2
All checks were successful
release / build-release (push) Successful in 1m46s
M8: progression, items, bankarta och sparfil
- internal/progress: katalog (skills.json 12 noder, items.json 25 items)
  med validering; XP/nivåer (150·n(n+1)/2) och skillpoäng; skillträd med
  krav och kostnad; items per plats med buffar, genvägar (JR Pass, kartor,
  powerbank) och färgton; banresultat och upplåsning (huvudbanor i ordning,
  sidospår efter föräldern); 33 %-regeln som test
- internal/save: versionerad JSON med kunskap, plånbok, debuffar,
  progression och pågående bana + seed; atomisk skrivning; migrering;
  ~/.config/metro-puzzle-jp/save.json resp. %AppData%
- sim: RunOptions får Extra/FogRadius/Landmarks/FreeGates/RevealAll/
  InfiniteBattery; luck påverkar trängselzonen; Fog.RevealAll och extra
  landmärken
- game: bankarta i spelbrädesstil ersätter menyn (⟳ återupptar med sparad
  seed), skillträd (T), garderob (I), butik i kiosken (B), XP/nivå/items/
  upplåsningar på resultatet, spelaren tonas av skins; autospar vid banstart,
  efter banan och vid köp; flaggor -save/-nosave
- level: kind/order/parent/rewards/shop; ny sidobana 002b Kabukicho som
  belönar skinnet Kimono; 001 belönar Salaryman-skinnet; butikssortiment
  på 002/003
- tools/shots: demo-sparfil (build/, aldrig den riktiga), bilder av karta,
  skills, garderob och butik; alla körningar med -nosave
- docs/README/wiki uppdaterade

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-24 11:29:30 +02:00

160 lines
5.9 KiB
Go

package game
import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/sim"
)
const stickDeadzone = 0.5
// dirInput returnerar hållen riktning från tangentbord (pilar/WASD) eller
// gamepad (d-pad / vänster spak). None om inget hålls.
func dirInput() sim.Dir {
if virtual.held != sim.None {
return virtual.held
}
switch {
case ebiten.IsKeyPressed(ebiten.KeyArrowUp) || ebiten.IsKeyPressed(ebiten.KeyW):
return sim.Up
case ebiten.IsKeyPressed(ebiten.KeyArrowDown) || ebiten.IsKeyPressed(ebiten.KeyS):
return sim.Down
case ebiten.IsKeyPressed(ebiten.KeyArrowLeft) || ebiten.IsKeyPressed(ebiten.KeyA):
return sim.Left
case ebiten.IsKeyPressed(ebiten.KeyArrowRight) || ebiten.IsKeyPressed(ebiten.KeyD):
return sim.Right
}
for _, id := range ebiten.AppendGamepadIDs(nil) {
if !ebiten.IsStandardGamepadLayoutAvailable(id) {
continue
}
switch {
case ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonLeftTop):
return sim.Up
case ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonLeftBottom):
return sim.Down
case ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonLeftLeft):
return sim.Left
case ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonLeftRight):
return sim.Right
}
x := ebiten.StandardGamepadAxisValue(id, ebiten.StandardGamepadAxisLeftStickHorizontal)
y := ebiten.StandardGamepadAxisValue(id, ebiten.StandardGamepadAxisLeftStickVertical)
switch {
case y < -stickDeadzone:
return sim.Up
case y > stickDeadzone:
return sim.Down
case x < -stickDeadzone:
return sim.Left
case x > stickDeadzone:
return sim.Right
}
}
return sim.None
}
// confirmJustPressed: Enter/mellanslag eller gamepadens nedre knapp (A/kryss).
func confirmJustPressed() bool {
if virtual.pressed["enter"] {
return true
}
if inpututil.IsKeyJustPressed(ebiten.KeyEnter) || inpututil.IsKeyJustPressed(ebiten.KeySpace) {
return true
}
return gamepadJustPressed(ebiten.StandardGamepadButtonRightBottom)
}
// backJustPressed: Escape eller gamepadens högra knapp (B/cirkel).
func backJustPressed() bool {
return virtual.pressed["esc"] || inpututil.IsKeyJustPressed(ebiten.KeyEscape) || gamepadJustPressed(ebiten.StandardGamepadButtonRightRight)
}
// helpJustPressed: H eller gamepadens övre knapp (Y/triangel) = fråga om hjälp.
func helpJustPressed() bool {
return virtual.pressed["h"] || inpututil.IsKeyJustPressed(ebiten.KeyH) || gamepadJustPressed(ebiten.StandardGamepadButtonRightTop)
}
// wordsJustPressed: L = ordlistan/språkträdet.
func wordsJustPressed() bool {
return virtual.pressed["l"] || inpututil.IsKeyJustPressed(ebiten.KeyL) || gamepadJustPressed(ebiten.StandardGamepadButtonLeftRight)
}
// miniMoveXY: just-tryck på pil/WASD/d-pad som (x, y) i -1/0/+1.
func miniMoveXY() (x, y int) {
switch {
case virtual.pressed["left"] || inpututil.IsKeyJustPressed(ebiten.KeyArrowLeft) || inpututil.IsKeyJustPressed(ebiten.KeyA) || gamepadJustPressed(ebiten.StandardGamepadButtonLeftLeft):
x = -1
case virtual.pressed["right"] || inpututil.IsKeyJustPressed(ebiten.KeyArrowRight) || inpututil.IsKeyJustPressed(ebiten.KeyD) || gamepadJustPressed(ebiten.StandardGamepadButtonLeftRight):
x = 1
}
switch {
case virtual.pressed["up"] || inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) || inpututil.IsKeyJustPressed(ebiten.KeyW) || gamepadJustPressed(ebiten.StandardGamepadButtonLeftTop):
y = -1
case virtual.pressed["down"] || inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) || inpututil.IsKeyJustPressed(ebiten.KeyS) || gamepadJustPressed(ebiten.StandardGamepadButtonLeftBottom):
y = 1
}
return x, y
}
// confirmHeld: Enter/mellanslag eller A hålls nere (sedelinmatning).
func confirmHeld() bool {
if virtual.pressed["enter"] || virtual.holdEnter {
return true
}
if ebiten.IsKeyPressed(ebiten.KeyEnter) || ebiten.IsKeyPressed(ebiten.KeySpace) {
return true
}
for _, id := range ebiten.AppendGamepadIDs(nil) {
if ebiten.IsStandardGamepadLayoutAvailable(id) && ebiten.IsStandardGamepadButtonPressed(id, ebiten.StandardGamepadButtonRightBottom) {
return true
}
}
return false
}
// skillsJustPressed: T = skillträdet.
func skillsJustPressed() bool { return virtual.pressed["t"] || inpututil.IsKeyJustPressed(ebiten.KeyT) }
// itemsJustPressed: I = garderoben/items.
func itemsJustPressed() bool { return virtual.pressed["i"] || inpututil.IsKeyJustPressed(ebiten.KeyI) }
// shopJustPressed: B = butiken i kiosken.
func shopJustPressed() bool { return virtual.pressed["b"] || inpututil.IsKeyJustPressed(ebiten.KeyB) }
// phoneJustPressed: P = kortet i telefonen av/på (meny).
func phoneJustPressed() bool { return virtual.pressed["p"] || inpututil.IsKeyJustPressed(ebiten.KeyP) }
// mapJustPressed: M eller gamepadens vänstra mittknapp (Back/Select) = minikartan av/på.
func mapJustPressed() bool {
return virtual.pressed["m"] || inpututil.IsKeyJustPressed(ebiten.KeyM) || gamepadJustPressed(ebiten.StandardGamepadButtonCenterLeft)
}
// menuMove returnerar -1/+1 när användaren just tryckte upp/ner i en meny.
func menuMove() int {
switch {
case virtual.pressed["up"]:
return -1
case virtual.pressed["down"]:
return 1
case inpututil.IsKeyJustPressed(ebiten.KeyArrowUp) || inpututil.IsKeyJustPressed(ebiten.KeyW) || gamepadJustPressed(ebiten.StandardGamepadButtonLeftTop):
return -1
case inpututil.IsKeyJustPressed(ebiten.KeyArrowDown) || inpututil.IsKeyJustPressed(ebiten.KeyS) || gamepadJustPressed(ebiten.StandardGamepadButtonLeftBottom):
return 1
}
return 0
}
func gamepadJustPressed(b ebiten.StandardGamepadButton) bool {
for _, id := range ebiten.AppendGamepadIDs(nil) {
if ebiten.IsStandardGamepadLayoutAvailable(id) && inpututil.IsStandardGamepadButtonJustPressed(id, b) {
return true
}
}
return false
}
// inpututil_R: R = spela om banan.
func inpututil_R() bool { return virtual.pressed["r"] || inpututil.IsKeyJustPressed(ebiten.KeyR) }