diff --git a/cmd/krampus19/context.go b/cmd/krampus19/context.go index ae32c40..caa7037 100644 --- a/cmd/krampus19/context.go +++ b/cmd/krampus19/context.go @@ -26,6 +26,7 @@ type Context struct { Textures map[string]texture Sprites map[string]sprite Levels map[string]levelPack + Progress progress SpriteDrawer SpriteDrawer Settings settings Palette *alui.Palette diff --git a/cmd/krampus19/game.go b/cmd/krampus19/game.go index 8177c0c..d734246 100644 --- a/cmd/krampus19/game.go +++ b/cmd/krampus19/game.go @@ -196,6 +196,10 @@ func (g *game) Init(disp *allg5.Display, settings settings, res vfs.CopyDir, con g.ctx = &Context{Resources: res, Textures: map[string]texture{}, Settings: settings, Navigation: navigation{game: g}} g.ctx.DisplaySize = geom.Pt(disp.Width(), disp.Height()) g.ctx.SpriteDrawer.ctx = g.ctx + err := g.ctx.Progress.load() + if err != nil { + log.Printf("Unable to load previous progress.") + } if err := g.initUI(disp, cons, fps); err != nil { return err } diff --git a/cmd/krampus19/io.go b/cmd/krampus19/io.go new file mode 100644 index 0000000..d308153 --- /dev/null +++ b/cmd/krampus19/io.go @@ -0,0 +1,50 @@ +package main + +import ( + "encoding/json" + "os" + "path/filepath" +) + +func decodeJSON(path string, v interface{}) error { + f, err := os.Open(path) + if err != nil { + return err + } + defer f.Close() + err = json.NewDecoder(f).Decode(v) + if err != nil { + return err + } + return nil +} + +func encodeJSON(path string, v interface{}) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + return json.NewEncoder(f).Encode(v) +} + +func userDir() (string, error) { + config, err := os.UserConfigDir() + if err != nil { + return "", err + } + dir := filepath.Join(config, "krampus19") + err = os.MkdirAll(dir, 0600) + if err != nil { + return "", err + } + return dir, nil +} + +func userFile(name string) (string, error) { + dir, err := userDir() + if err != nil { + return "", err + } + return filepath.Join(dir, name), nil +} diff --git a/cmd/krampus19/krampus19.go b/cmd/krampus19/krampus19.go index 8c7e198..69a7d10 100644 --- a/cmd/krampus19/krampus19.go +++ b/cmd/krampus19/krampus19.go @@ -48,7 +48,7 @@ func run() error { } err = settings.StoreDefault() if err != nil { - log.Printf("Unable to store settings.") + log.Printf("Unable to store settings; err: %v", err) } log.Printf("Creating display.") diff --git a/cmd/krampus19/levelselect.go b/cmd/krampus19/levelselect.go index 42ccc79..342c96a 100644 --- a/cmd/krampus19/levelselect.go +++ b/cmd/krampus19/levelselect.go @@ -19,10 +19,15 @@ func (s *levelSelect) Enter(ctx *Context) error { s.ctx = ctx s.pack = s.ctx.Levels[s.packID] s.Init() - name := func(id string) string { return fmt.Sprintf("Level %s", id) } + name := func(id string, steps int) string { + if steps == 0 { + return fmt.Sprintf("Level %s", id) + } + return fmt.Sprintf("Level %s (%d)", id, steps) + } for _, id := range s.pack.order { levelID := id - s.Add(name(levelID), func() { + s.Add(name(levelID, s.ctx.Progress.Level(s.packID, levelID).Steps), func() { s.ctx.Navigation.PlayLevel(s.packID, levelID) }) } diff --git a/cmd/krampus19/playlevel.go b/cmd/krampus19/playlevel.go index a85f158..4e0be1d 100644 --- a/cmd/krampus19/playlevel.go +++ b/cmd/krampus19/playlevel.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log" "sort" "opslag.de/schobers/allg5" @@ -60,7 +61,22 @@ func (l *playLevel) Leave() {} func (l *playLevel) onComplete() { menu := alui.NewMenu() menu.AddChild(alui.NewMargins(&alui.Label{ControlBase: alui.ControlBase{Font: "header"}, Text: "Congratulations", TextAlign: allg5.AlignCenter}, 3*margin, 0, 2*margin)) - menu.AddChild(alui.NewMargins(&alui.Label{Text: fmt.Sprintf("You've completed the level in %d steps", l.state.steps), TextAlign: allg5.AlignCenter}, 0, 0, 2*margin)) + menu.AddChild(alui.NewMargins(&alui.Label{Text: fmt.Sprintf("You've completed the level in %d steps.", l.state.steps), TextAlign: allg5.AlignCenter}, 0, 0, 2*margin)) + + progress := l.ctx.Progress.Level(l.packID, l.levelID) + if l.state.steps < progress.Steps || progress.Steps == 0 { + var improvement = progress.Steps - l.state.steps + if improvement > 0 { // first time it is lower than zero (no previous record). + menu.AddChild(alui.NewMargins(&alui.Label{Text: fmt.Sprintf("This is an improvement of %d steps.", improvement), TextAlign: allg5.AlignCenter}, 0, 0, 2*margin)) + } + + err := l.ctx.Progress.Update(l.packID, l.levelID, func(p *levelProgress) { + p.Steps = l.state.steps + }) + if err != nil { + log.Printf("Unable to store progress; err: %v", err) + } + } nextID, ok := l.state.pack.FindNext(l.levelID) if ok { diff --git a/cmd/krampus19/progress.go b/cmd/krampus19/progress.go new file mode 100644 index 0000000..85fe908 --- /dev/null +++ b/cmd/krampus19/progress.go @@ -0,0 +1,67 @@ +package main + +type progress struct { + Packs map[string]packProgress +} + +func (p *progress) Pack(packID string) packProgress { + if p.Packs == nil { + p.Packs = map[string]packProgress{} + } + return p.Packs[packID] +} + +func (p *progress) Level(packID, levelID string) levelProgress { + pack := p.Pack(packID) + return pack.Level(levelID) +} + +func (p *progress) Update(packID, levelID string, update func(*levelProgress)) error { + pack := p.Pack(packID) + pack.Update(levelID, update) + p.Packs[packID] = pack + return p.store() +} + +type packProgress struct { + Levels map[string]levelProgress +} + +func (p *packProgress) Level(id string) levelProgress { + if p.Levels == nil { + p.Levels = map[string]levelProgress{} + } + return p.Levels[id] +} + +func (p *packProgress) Update(id string, update func(*levelProgress)) { + level := p.Level(id) + update(&level) + p.Levels[id] = level +} + +type levelProgress struct { + Steps int +} + +func (p *progress) store() error { + path, err := userFile("progress.json") + if err != nil { + return err + } + return encodeJSON(path, p) +} + +func (p *progress) load() error { + path, err := userFile("progress.json") + if err != nil { + return err + } + var fromFile progress + err = decodeJSON(path, &fromFile) + if err != nil { + return err + } + *p = fromFile + return nil +} diff --git a/cmd/krampus19/settings.go b/cmd/krampus19/settings.go index 35ceb4f..d23e37a 100644 --- a/cmd/krampus19/settings.go +++ b/cmd/krampus19/settings.go @@ -1,10 +1,6 @@ package main import ( - "encoding/json" - "os" - "path/filepath" - "opslag.de/schobers/allg5" ) @@ -28,27 +24,11 @@ type settings struct { Video video } -func (s *settings) DefaultPath() (string, error) { - config, err := os.UserConfigDir() - if err != nil { - return "", err - } - dir := filepath.Join(config, "krampus19") - err = os.MkdirAll(dir, 0600) - if err != nil { - return "", err - } - return filepath.Join(dir, "settings.json"), nil -} +func (s *settings) DefaultPath() (string, error) { return userFile("settings.json") } func (s *settings) Load(path string) error { - f, err := os.Open(path) - if err != nil { - return err - } - defer f.Close() var fromFile settings - err = json.NewDecoder(f).Decode(&fromFile) + err := decodeJSON(path, &fromFile) if err != nil { return err } @@ -64,14 +44,7 @@ func (s *settings) LoadDefault() error { return s.Load(path) } -func (s *settings) Store(path string) error { - f, err := os.Create(path) - if err != nil { - return err - } - defer f.Close() - return json.NewEncoder(f).Encode(&s) -} +func (s *settings) Store(path string) error { return encodeJSON(path, s) } func (s *settings) StoreDefault() error { path, err := s.DefaultPath()