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>
114 lines
3.1 KiB
Go
114 lines
3.1 KiB
Go
package game
|
|
|
|
import (
|
|
"fmt"
|
|
"image/color"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
|
|
|
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/progress"
|
|
)
|
|
|
|
// SkillsScene är skillträdet: grenar med noder, kostnad i skillpoäng och krav.
|
|
type SkillsScene struct {
|
|
back Scene
|
|
skills []progress.Skill
|
|
sel int
|
|
top int
|
|
msg string
|
|
}
|
|
|
|
const skillRows = 8
|
|
|
|
// NewSkillsScene listar skills gren för gren.
|
|
func NewSkillsScene(g *Game, back Scene) *SkillsScene {
|
|
sk := append([]progress.Skill(nil), g.Catalog.Skills...)
|
|
sort.SliceStable(sk, func(i, j int) bool {
|
|
if sk[i].Branch != sk[j].Branch {
|
|
return sk[i].Branch < sk[j].Branch
|
|
}
|
|
return len(sk[i].Requires) < len(sk[j].Requires)
|
|
})
|
|
return &SkillsScene{back: back, skills: sk}
|
|
}
|
|
|
|
func (s *SkillsScene) Update(g *Game) error {
|
|
if backJustPressed() || skillsJustPressed() {
|
|
g.SetScene(s.back)
|
|
return nil
|
|
}
|
|
if n := len(s.skills); n > 0 {
|
|
s.sel = (s.sel + menuMove() + n) % n
|
|
if s.sel < s.top {
|
|
s.top = s.sel
|
|
}
|
|
if s.sel >= s.top+skillRows {
|
|
s.top = s.sel - skillRows + 1
|
|
}
|
|
if confirmJustPressed() {
|
|
sk := s.skills[s.sel]
|
|
switch {
|
|
case g.Progress.Has(sk.ID):
|
|
s.msg = g.Assets.T("skills.already")
|
|
case g.Progress.Unlock(g.Catalog, sk.ID):
|
|
s.msg = fmt.Sprintf(g.Assets.T("skills.unlocked"), sk.Name.Get(UILang))
|
|
g.Save()
|
|
case g.Progress.SkillPoints < sk.Cost:
|
|
s.msg = fmt.Sprintf(g.Assets.T("skills.need_points"), sk.Cost)
|
|
default:
|
|
s.msg = g.Assets.T("skills.need_req")
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *SkillsScene) Draw(g *Game, screen *ebiten.Image) {
|
|
a := g.Assets
|
|
p := g.Progress
|
|
screen.Fill(color.RGBA{24, 20, 37, 255})
|
|
vector.DrawFilledRect(screen, 0, 0, ScreenW, 22, color.RGBA{38, 43, 68, 255}, false)
|
|
a.DrawBig(screen, a.T("skills.title"), 8, 5, colText)
|
|
hdr := fmt.Sprintf("%s %d %s %d %s %d", a.T("map.skill_points"), p.SkillPoints, a.T("map.level"), p.Level(), a.T("skills.to_next"), progress_XPToNext(p.XP))
|
|
a.Draw(screen, hdr, ScreenW-8-a.Width(hdr), 6, colKnown)
|
|
|
|
y0 := 30
|
|
for i := s.top; i < len(s.skills) && i < s.top+skillRows; i++ {
|
|
sk := s.skills[i]
|
|
y := y0 + (i-s.top)*LineH
|
|
if i == s.sel {
|
|
vector.DrawFilledRect(screen, 4, float32(y-1), ScreenW-8, LineH, colBar, false)
|
|
}
|
|
col, status := colDim, strings.Repeat("●", sk.Cost)
|
|
switch {
|
|
case p.Has(sk.ID):
|
|
col, status = colGood, "✓"
|
|
case p.CanUnlock(g.Catalog, sk.ID):
|
|
col = colKnown
|
|
}
|
|
a.Draw(screen, sk.Branch, 8, y, colDim)
|
|
a.Draw(screen, sk.Name.Get(UILang), 80, y, col)
|
|
if len(sk.Requires) > 0 {
|
|
req := make([]string, len(sk.Requires))
|
|
for k, r := range sk.Requires {
|
|
req[k] = g.Catalog.Skill(r).Name.Get(UILang)
|
|
}
|
|
a.Draw(screen, "← "+strings.Join(req, ", "), 190, y, colDim)
|
|
}
|
|
a.Draw(screen, status, ScreenW-8-a.Width(status), y, col)
|
|
}
|
|
if len(s.skills) > 0 {
|
|
sel := s.skills[s.sel]
|
|
for i, l := range a.wrapLine(plainLine(sel.Desc.Get(UILang), colText), ScreenW-16) {
|
|
a.drawLines(screen, []line{l}, 8, 130+i*LineH)
|
|
}
|
|
}
|
|
if s.msg != "" {
|
|
a.Draw(screen, s.msg, 8, ScreenH-26, colKnown)
|
|
}
|
|
a.Draw(screen, a.T("skills.hint"), 8, ScreenH-13, colDim)
|
|
}
|