M8: progression, items, bankarta och sparfil
All checks were successful
release / build-release (push) Successful in 1m46s
All checks were successful
release / build-release (push) Successful in 1m46s
- 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>
This commit is contained in:
@@ -36,6 +36,9 @@ type LevelScene struct {
|
||||
animT float64
|
||||
hideMap bool // M döljer/visar minikartan
|
||||
|
||||
shop *shop // öppen butik i kiosken (pausar banan)
|
||||
outcome LevelOutcome // progression från banan (fylls i när den avgjorts)
|
||||
|
||||
// Tåg och effekter (M7b).
|
||||
tracks []trackInfo
|
||||
rails map[level.Point]bool
|
||||
@@ -45,15 +48,16 @@ type LevelScene struct {
|
||||
}
|
||||
|
||||
// NewLevelScene laddar banan och ställer spelaren på startrutan. Kunskap,
|
||||
// plånbok och debuffar tas från Game (delas mellan banor); seed styr slumpen.
|
||||
func NewLevelScene(a *Assets, id string, g *Game, seed rng.Seed) (*LevelScene, error) {
|
||||
// plånbok, debuffar, skills och items tas från Game; seed styr slumpen.
|
||||
func NewLevelScene(g *Game, id string, seed rng.Seed) (*LevelScene, error) {
|
||||
a := g.Assets
|
||||
lvl, err := level.LoadFile(filepath.Join(a.LevelsDir, id+".json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := &LevelScene{
|
||||
id: id,
|
||||
run: sim.NewRun(lvl, sim.RunOptions{Dict: a.Dict, Know: g.Know, Wallet: g.Wallet, Debuffs: g.Profile.Debuffs, Seed: seed}),
|
||||
run: sim.NewRun(lvl, g.runOptions(id, seed)),
|
||||
cam: sim.NewCamera(ScreenW, ScreenH, float64(lvl.Width()*TileSize), float64(lvl.Height()*TileSize)),
|
||||
}
|
||||
s.cam.Margin = TileSize // rälsen ligger i kantraden: visa en ruta utanför kartan
|
||||
@@ -102,23 +106,27 @@ func (s *LevelScene) followPlayer() {
|
||||
}
|
||||
|
||||
func (s *LevelScene) Update(g *Game) error {
|
||||
if s.shop != nil { // butiken pausar banan
|
||||
s.updateShop(g)
|
||||
return nil
|
||||
}
|
||||
s.updateEffects(g)
|
||||
if s.run.Mini != nil {
|
||||
// Minispel: all input går dit; Esc avbryter minispelet, inte banan.
|
||||
mx, my := miniMoveXY()
|
||||
s.run.StepMini(dt, minigame.Input{Move: my, MoveX: mx, Confirm: confirmJustPressed(), Hold: confirmHeld(), Cancel: backJustPressed()})
|
||||
if s.run.Outcome != sim.Ongoing {
|
||||
g.levelEnded(s.run)
|
||||
s.outcome = g.levelEnded(s.run, s.id)
|
||||
g.SetScene(NewResultScene(s))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if backJustPressed() {
|
||||
m, err := NewMenuScene(g.Assets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.SetScene(m)
|
||||
g.SetScene(NewMapScene(g))
|
||||
return nil
|
||||
}
|
||||
if shopJustPressed() && !s.run.Player.Moving() && s.run.Level.At(s.run.Player.Tile.X, s.run.Player.Tile.Y) == level.Kiosk {
|
||||
s.openShop(g)
|
||||
return nil
|
||||
}
|
||||
if helpJustPressed() {
|
||||
@@ -135,7 +143,7 @@ func (s *LevelScene) Update(g *Game) error {
|
||||
}
|
||||
s.followPlayer()
|
||||
if s.run.Outcome != sim.Ongoing {
|
||||
g.levelEnded(s.run)
|
||||
s.outcome = g.levelEnded(s.run, s.id)
|
||||
g.SetScene(NewResultScene(s))
|
||||
}
|
||||
return nil
|
||||
@@ -227,7 +235,11 @@ func (s *LevelScene) Draw(g *Game, screen *ebiten.Image) {
|
||||
px, py := leader.WorldPos(TileSize)
|
||||
op.GeoM.Reset()
|
||||
op.GeoM.Translate(ox+math.Round(px), oy+math.Round(py))
|
||||
tint := g.Effects().Tint
|
||||
op.ColorScale.Reset()
|
||||
op.ColorScale.Scale(tint[0], tint[1], tint[2], 1)
|
||||
screen.DrawImage(g.Assets.PlayerFrame(leader.Facing, leader.Moving(), s.animT), &op)
|
||||
op.ColorScale.Reset()
|
||||
s.drawUrge(g, screen, s.run.Party.Members[0], int(ox+math.Round(px)), int(oy+math.Round(py)))
|
||||
s.drawIndicator(screen, ox, oy)
|
||||
|
||||
@@ -235,6 +247,9 @@ func (s *LevelScene) Draw(g *Game, screen *ebiten.Image) {
|
||||
if s.run.Mini != nil {
|
||||
s.drawMinigame(g, screen)
|
||||
}
|
||||
if s.shop != nil {
|
||||
s.drawShop(g, screen)
|
||||
}
|
||||
}
|
||||
|
||||
// memberTints färgar följarnas sprites tills de har egna.
|
||||
@@ -308,8 +323,8 @@ func (s *LevelScene) drawHUD(g *Game, screen *ebiten.Image) {
|
||||
a.Draw(screen, wallet, 4, LineH, walletCol)
|
||||
s.drawMeters(g, screen)
|
||||
|
||||
if r.Outcome != sim.Ongoing || r.Mini != nil {
|
||||
return // resultatpanelen eller minispelet tar över; ingen inforuta eller minikarta
|
||||
if r.Outcome != sim.Ongoing || r.Mini != nil || s.shop != nil {
|
||||
return // resultatpanelen, minispelet eller butiken tar över; ingen inforuta eller minikarta
|
||||
}
|
||||
lines, accent, bar := s.infoLines(a)
|
||||
if entry := a.Dict.Lookup(r.Learned); entry != nil && r.Time-r.LearnedAt < learnedShowSec {
|
||||
@@ -457,9 +472,9 @@ func (s *LevelScene) infoLines(a *Assets) (lines []line, accent color.Color, bar
|
||||
return []line{plainLine(a.T("hud.toilet_none"), colDim)}, accent, false
|
||||
case level.Kiosk:
|
||||
if n := len(r.Party.NeedsKiosk()); n > 0 {
|
||||
return []line{plainLine(fmt.Sprintf(a.T("hud.kiosk_prompt"), n, sim.KioskPriceYen*n), colGood)}, colGood, false
|
||||
return []line{plainLine(fmt.Sprintf(a.T("hud.kiosk_prompt"), n, sim.KioskPriceYen*n)+" "+a.T("hud.shop_prompt"), colGood)}, colGood, false
|
||||
}
|
||||
return []line{plainLine(a.T("hud.kiosk_none"), colDim)}, accent, false
|
||||
return []line{plainLine(a.T("hud.kiosk_none")+" "+a.T("hud.shop_prompt"), colDim)}, accent, false
|
||||
case level.TicketMachine:
|
||||
if lvl.TicketRequired {
|
||||
return []line{plainLine(fmt.Sprintf(a.T("hud.machine_ticket"), r.Wallet.Cash), colGood)}, colGood, false
|
||||
|
||||
Reference in New Issue
Block a user