Add resume play.
Add highscores.
This commit is contained in:
parent
c628ae4b09
commit
4f1760ad57
2
TODO.md
2
TODO.md
@ -1,6 +1,6 @@
|
|||||||
- [X] Increase difficulty.
|
- [X] Increase difficulty.
|
||||||
- [ ] Add music & sounds.
|
- [ ] Add music & sounds.
|
||||||
- [ ] Keep score/difficulty level (resume & restart).
|
- [X] Keep score/difficulty level (resume & restart).
|
||||||
- [X] ~~Explain controls on info page~~ add settings for controls.
|
- [X] ~~Explain controls on info page~~ add settings for controls.
|
||||||
- [X] Fix usage of go/embed (and remove rice again).
|
- [X] Fix usage of go/embed (and remove rice again).
|
||||||
- [X] Add monster animations (~~jumping on tile &~~ towards new tile).
|
- [X] Add monster animations (~~jumping on tile &~~ towards new tile).
|
||||||
|
@ -10,6 +10,7 @@ type app struct {
|
|||||||
ui.Proxy
|
ui.Proxy
|
||||||
|
|
||||||
settings *tins2021.Settings
|
settings *tins2021.Settings
|
||||||
|
score *tins2021.ScoreState
|
||||||
|
|
||||||
context *appContext
|
context *appContext
|
||||||
}
|
}
|
||||||
@ -55,7 +56,7 @@ func (a *app) Init(ctx ui.Context) error {
|
|||||||
|
|
||||||
ctx.Overlays().AddOnTop(fpsOverlayName, &play.FPS{Align: ui.AlignRight}, false)
|
ctx.Overlays().AddOnTop(fpsOverlayName, &play.FPS{Align: ui.AlignRight}, false)
|
||||||
|
|
||||||
a.context = newAppContext(ctx, a.settings, func(control ui.Control) {
|
a.context = newAppContext(ctx, a.settings, a.score, func(control ui.Control) {
|
||||||
a.Content = control
|
a.Content = control
|
||||||
})
|
})
|
||||||
a.context.ShowMainMenu(ctx)
|
a.context.ShowMainMenu(ctx)
|
||||||
|
@ -9,6 +9,7 @@ type appContext struct {
|
|||||||
setView func(ui.Control)
|
setView func(ui.Control)
|
||||||
|
|
||||||
Settings *tins2021.Settings
|
Settings *tins2021.Settings
|
||||||
|
Score *tins2021.ScoreState
|
||||||
Debug bool
|
Debug bool
|
||||||
|
|
||||||
StarTexture tins2021.AnimatedTexture
|
StarTexture tins2021.AnimatedTexture
|
||||||
@ -17,11 +18,12 @@ type appContext struct {
|
|||||||
MonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture
|
MonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAppContext(ctx ui.Context, settings *tins2021.Settings, setView func(ui.Control)) *appContext {
|
func newAppContext(ctx ui.Context, settings *tins2021.Settings, score *tins2021.ScoreState, setView func(ui.Control)) *appContext {
|
||||||
textures := textureGenerator{}
|
textures := textureGenerator{}
|
||||||
app := &appContext{
|
app := &appContext{
|
||||||
setView: setView,
|
setView: setView,
|
||||||
Settings: settings,
|
Settings: settings,
|
||||||
|
Score: score,
|
||||||
StarTexture: newAnimatedTexture(ctx, "star", defaultAnimationFrames, textures.Star),
|
StarTexture: newAnimatedTexture(ctx, "star", defaultAnimationFrames, textures.Star),
|
||||||
HeartTexture: newAnimatedTexture(ctx, "heart", defaultAnimationFrames, textures.Heart),
|
HeartTexture: newAnimatedTexture(ctx, "heart", defaultAnimationFrames, textures.Heart),
|
||||||
MonsterTextures: map[tins2021.MonsterType]tins2021.AnimatedTexture{
|
MonsterTextures: map[tins2021.MonsterType]tins2021.AnimatedTexture{
|
||||||
@ -40,6 +42,7 @@ func (app *appContext) Play(ctx ui.Context) {
|
|||||||
level := tins2021.NewLevel()
|
level := tins2021.NewLevel()
|
||||||
level.Randomize(0, numberOfStars)
|
level.Randomize(0, numberOfStars)
|
||||||
|
|
||||||
|
app.Score.Current = tins2021.Score{}
|
||||||
app.show(newLevelControl(app, ctx, level))
|
app.show(newLevelControl(app, ctx, level))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +59,14 @@ func (app *appContext) PlayNext(ctx ui.Context, controller *levelController) {
|
|||||||
controller.Play(level)
|
controller.Play(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *appContext) PlayResume(ctx ui.Context) {
|
||||||
|
level := tins2021.NewLevel()
|
||||||
|
level.Score = app.Score.Current.Score
|
||||||
|
level.Randomize(app.Score.Current.Difficulty, numberOfStars)
|
||||||
|
|
||||||
|
app.show(newLevelControl(app, ctx, level))
|
||||||
|
}
|
||||||
|
|
||||||
func (app *appContext) show(control ui.Control) {
|
func (app *appContext) show(control ui.Control) {
|
||||||
app.setView(control)
|
app.setView(control)
|
||||||
}
|
}
|
||||||
@ -68,6 +79,10 @@ func (app *appContext) ShowSettings(ctx ui.Context) {
|
|||||||
app.setView(newSettings(app, ctx))
|
app.setView(newSettings(app, ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *appContext) ShowHighscores(ctx ui.Context) {
|
||||||
|
app.setView(newHighscores(app, ctx))
|
||||||
|
}
|
||||||
|
|
||||||
func (app *appContext) ShowInfo(ctx ui.Context) {
|
func (app *appContext) ShowInfo(ctx ui.Context) {
|
||||||
app.setView(newInfo(app, ctx))
|
app.setView(newInfo(app, ctx))
|
||||||
}
|
}
|
||||||
|
52
cmd/tins2021/highscores.go
Normal file
52
cmd/tins2021/highscores.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"opslag.de/schobers/zntg/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type highscores struct {
|
||||||
|
ui.StackPanel
|
||||||
|
|
||||||
|
app *appContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHighscores(app *appContext, ctx ui.Context) ui.Control {
|
||||||
|
scores := ui.BuildStackPanel(ui.OrientationVertical, func(p *ui.StackPanel) {
|
||||||
|
p.AddChild(label("RANK", "default"))
|
||||||
|
for i, score := range app.Score.Highscores {
|
||||||
|
p.AddChild(label(fmt.Sprintf("%d. %d", i+1, score.Score), "score"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
difficulties := ui.BuildStackPanel(ui.OrientationVertical, func(p *ui.StackPanel) {
|
||||||
|
p.AddChild(label(" DIFFICULTY", "default"))
|
||||||
|
for _, score := range app.Score.Highscores {
|
||||||
|
p.AddChild(labelOpts(fmt.Sprintf("%d", score.Difficulty), "score", labelOptions{TextAlignment: ui.AlignRight}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
content := []ui.Control{
|
||||||
|
labelOpts("HIGHSCORES", "title", labelOptions{TextAlignment: ui.AlignCenter}),
|
||||||
|
label("", "score"),
|
||||||
|
Center(ui.BuildStackPanel(ui.OrientationHorizontal, func(p *ui.StackPanel) {
|
||||||
|
p.AddChild(scores, difficulties)
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
|
||||||
|
return Center(&highscores{StackPanel: ui.StackPanel{
|
||||||
|
ContainerBase: ui.ContainerBase{
|
||||||
|
Children: content,
|
||||||
|
},
|
||||||
|
Orientation: ui.OrientationVertical,
|
||||||
|
}, app: app})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *highscores) Handle(ctx ui.Context, e ui.Event) bool {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *ui.KeyDownEvent:
|
||||||
|
if e.Key == ui.KeyEscape || e.Key == ui.KeyEnter {
|
||||||
|
i.app.ShowMainMenu(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i.ControlBase.Handle(ctx, e)
|
||||||
|
}
|
@ -30,6 +30,8 @@ type levelController struct {
|
|||||||
SmallFont *tins2021.BitmapFont
|
SmallFont *tins2021.BitmapFont
|
||||||
|
|
||||||
Controls map[ui.Key]tins2021.Direction
|
Controls map[ui.Key]tins2021.Direction
|
||||||
|
|
||||||
|
Highscore bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLevelControl(app *appContext, ctx ui.Context, level *tins2021.Level) *levelController {
|
func newLevelControl(app *appContext, ctx ui.Context, level *tins2021.Level) *levelController {
|
||||||
@ -84,11 +86,23 @@ func IsModifierPressed(modifiers ui.KeyModifier, pressed ui.KeyModifier) bool {
|
|||||||
return modifiers&pressed == pressed
|
return modifiers&pressed == pressed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *levelController) updateHighscore() bool {
|
||||||
|
highscores, highscore := r.app.Score.Highscores.AddScore(r.Level.Score, r.Level.Difficulty)
|
||||||
|
if highscore {
|
||||||
|
r.app.Score.Highscores = highscores
|
||||||
|
}
|
||||||
|
r.app.Score.Current = tins2021.Score{} // reset score
|
||||||
|
return highscore
|
||||||
|
}
|
||||||
|
|
||||||
func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool {
|
func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool {
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *ui.KeyDownEvent:
|
case *ui.KeyDownEvent:
|
||||||
switch e.Key {
|
switch e.Key {
|
||||||
case ui.KeyEscape:
|
case ui.KeyEscape:
|
||||||
|
if r.Level.StarsCollected == r.Level.Stars {
|
||||||
|
r.app.Score.Current = tins2021.NewScore(r.Level.Score, r.Level.Difficulty+1)
|
||||||
|
}
|
||||||
r.app.ShowMainMenu(ctx)
|
r.app.ShowMainMenu(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,6 +116,7 @@ func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool {
|
|||||||
case *ui.KeyDownEvent:
|
case *ui.KeyDownEvent:
|
||||||
switch e.Key {
|
switch e.Key {
|
||||||
case ui.KeyEnter:
|
case ui.KeyEnter:
|
||||||
|
r.app.Score.Current = tins2021.NewScore(r.Level.Score, r.Level.Difficulty+1)
|
||||||
r.app.PlayNext(ctx, r)
|
r.app.PlayNext(ctx, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,6 +128,9 @@ func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool {
|
|||||||
dir, ok := r.Controls[e.Key]
|
dir, ok := r.Controls[e.Key]
|
||||||
if ok {
|
if ok {
|
||||||
r.Level.MovePlayer(dir)
|
r.Level.MovePlayer(dir)
|
||||||
|
if r.Level.GameOver {
|
||||||
|
r.Highscore = r.updateHighscore()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +149,9 @@ func (r *levelController) Handle(ctx ui.Context, e ui.Event) bool {
|
|||||||
r.Level.DestroyMonster(pos)
|
r.Level.DestroyMonster(pos)
|
||||||
jumped = append(jumped, pos)
|
jumped = append(jumped, pos)
|
||||||
r.Level.DecrementLive()
|
r.Level.DecrementLive()
|
||||||
|
if r.Level.GameOver {
|
||||||
|
r.Highscore = r.updateHighscore()
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if animation.Frame < 50 { // after 50 frames the animation has finished
|
if animation.Frame < 50 { // after 50 frames the animation has finished
|
||||||
@ -328,6 +349,12 @@ func (r levelController) Render(ctx ui.Context) {
|
|||||||
offsetY := .5*bounds.Dy() - titleFont.Height()
|
offsetY := .5*bounds.Dy() - titleFont.Height()
|
||||||
renderer.TextAlign(titleFont, geom.PtF32(centerX, offsetY), textColor, "GAME OVER", ui.AlignCenter)
|
renderer.TextAlign(titleFont, geom.PtF32(centerX, offsetY), textColor, "GAME OVER", ui.AlignCenter)
|
||||||
|
|
||||||
|
if r.Highscore {
|
||||||
|
highscoreFont := ctx.Fonts().Font("default")
|
||||||
|
offsetY += titleFont.Height() + scoreFont.Height()
|
||||||
|
renderer.TextAlign(highscoreFont, geom.PtF32(centerX, offsetY), ctx.Style().Palette.Primary, "NEW HIGHSCORE", ui.AlignCenter)
|
||||||
|
}
|
||||||
|
|
||||||
offsetY += titleFont.Height() + scoreFont.Height()
|
offsetY += titleFont.Height() + scoreFont.Height()
|
||||||
renderer.TextAlign(scoreFont, geom.PtF32(centerX, offsetY), textColor, fmt.Sprintf("Final score: %d", r.Level.Score), ui.AlignCenter)
|
renderer.TextAlign(scoreFont, geom.PtF32(centerX, offsetY), textColor, fmt.Sprintf("Final score: %d", r.Level.Score), ui.AlignCenter)
|
||||||
|
|
||||||
|
@ -37,6 +37,10 @@ func newMainMenu(app *appContext, ctx ui.Context) ui.Control {
|
|||||||
menu.Add("Play", func(ctx ui.Context) {
|
menu.Add("Play", func(ctx ui.Context) {
|
||||||
app.Play(ctx)
|
app.Play(ctx)
|
||||||
})
|
})
|
||||||
|
if app.Score.Current.Difficulty > 0 {
|
||||||
|
menu.Add("Resume", func(ctx ui.Context) { app.PlayResume(ctx) })
|
||||||
|
}
|
||||||
|
menu.Add("Highscores", func(ctx ui.Context) { app.ShowHighscores(ctx) })
|
||||||
menu.Add("Controls", func(ctx ui.Context) { app.ShowSettings(ctx) })
|
menu.Add("Controls", func(ctx ui.Context) { app.ShowSettings(ctx) })
|
||||||
menu.Add("Credits", func(ctx ui.Context) { app.ShowCredits(ctx) })
|
menu.Add("Credits", func(ctx ui.Context) { app.ShowCredits(ctx) })
|
||||||
menu.Add("Quit", func(ctx ui.Context) { ctx.Quit() })
|
menu.Add("Quit", func(ctx ui.Context) { ctx.Quit() })
|
||||||
|
@ -50,6 +50,11 @@ func run() error {
|
|||||||
}
|
}
|
||||||
defer settings.Store()
|
defer settings.Store()
|
||||||
|
|
||||||
|
score, err := tins2021.LoadScores()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("unable to load score; error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
var location *geom.PointF32
|
var location *geom.PointF32
|
||||||
if settings.Window.Location != nil {
|
if settings.Window.Location != nil {
|
||||||
location = &geom.PointF32{X: float32(settings.Window.Location.X), Y: float32(settings.Window.Location.Y)}
|
location = &geom.PointF32{X: float32(settings.Window.Location.X), Y: float32(settings.Window.Location.Y)}
|
||||||
@ -74,7 +79,10 @@ func run() error {
|
|||||||
|
|
||||||
app := &app{
|
app := &app{
|
||||||
settings: settings,
|
settings: settings,
|
||||||
|
score: &score,
|
||||||
}
|
}
|
||||||
|
defer tins2021.SaveScores(app.score)
|
||||||
|
|
||||||
style := ui.DefaultStyle()
|
style := ui.DefaultStyle()
|
||||||
style.Palette = &ui.Palette{
|
style.Palette = &ui.Palette{
|
||||||
Background: zntg.MustHexColor(`#494949`),
|
Background: zntg.MustHexColor(`#494949`),
|
||||||
|
36
io.go
36
io.go
@ -1,11 +1,43 @@
|
|||||||
package tins2021
|
package tins2021
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
|
||||||
"opslag.de/schobers/zntg"
|
"opslag.de/schobers/zntg"
|
||||||
)
|
)
|
||||||
|
|
||||||
const appName = "tins2021_qbitter"
|
const appName = "tins2021_qbitter"
|
||||||
|
|
||||||
func UserDir() (string, error) { return zntg.UserDir(appName) }
|
func UserDir() (string, error) { return zntg.UserConfigDir(appName) }
|
||||||
|
|
||||||
func UserFile(name string) (string, error) { return zntg.UserFile(appName, name) }
|
func UserFile(name string) (string, error) { return zntg.UserConfigFile(appName, name) }
|
||||||
|
|
||||||
|
func LoadUserFileJSON(name string, v interface{}) error {
|
||||||
|
path, err := UserFile(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
|
return os.ErrNotExist
|
||||||
|
}
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return json.NewDecoder(f).Decode(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveUserFileJSON(name string, v interface{}) error {
|
||||||
|
path, err := UserFile(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return json.NewEncoder(f).Encode(v)
|
||||||
|
}
|
||||||
|
83
score.go
Normal file
83
score.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package tins2021
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const scoreFileName = "score.json"
|
||||||
|
|
||||||
|
type Highscores []Score
|
||||||
|
|
||||||
|
func (h Highscores) AddScore(score, difficulty int) (Highscores, bool) {
|
||||||
|
highscores := len(h)
|
||||||
|
var rank = highscores
|
||||||
|
for ; rank > 0; rank-- {
|
||||||
|
if score <= h[rank-1].Score {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
highscore := NewScore(score, difficulty)
|
||||||
|
if rank == highscores && highscores < 10 {
|
||||||
|
return append(h, highscore), true
|
||||||
|
}
|
||||||
|
if rank < 10 {
|
||||||
|
return append(h[:rank], append([]Score{highscore}, h[rank:highscores-1]...)...), true
|
||||||
|
}
|
||||||
|
return h, false
|
||||||
|
}
|
||||||
|
|
||||||
|
type Score struct {
|
||||||
|
Score int
|
||||||
|
Difficulty int
|
||||||
|
Hash string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScore(score, difficulty int) Score {
|
||||||
|
s := Score{Score: score, Difficulty: difficulty}
|
||||||
|
s.Hash = s.hash()
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Score) hash() string {
|
||||||
|
hashText := fmt.Sprintf("tins2021_qbitter, %d, %d", s.Score, s.Difficulty)
|
||||||
|
hash := sha256.Sum256([]byte(hashText))
|
||||||
|
return base64.StdEncoding.EncodeToString(hash[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Score) Validate() bool {
|
||||||
|
hash := s.hash()
|
||||||
|
if hash == s.Hash {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
s.Score = 0
|
||||||
|
s.Difficulty = 0
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScoreState struct {
|
||||||
|
Current Score
|
||||||
|
Highscores Highscores
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadScores() (ScoreState, error) {
|
||||||
|
var state ScoreState
|
||||||
|
if err := LoadUserFileJSON(scoreFileName, &state); err != nil {
|
||||||
|
return ScoreState{}, err
|
||||||
|
}
|
||||||
|
state.Current.Validate()
|
||||||
|
for i := 0; i < len(state.Highscores); {
|
||||||
|
if !state.Highscores[i].Validate() {
|
||||||
|
state.Highscores = append(state.Highscores[:i], state.Highscores[i+1:]...)
|
||||||
|
} else {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveScores(s *ScoreState) error {
|
||||||
|
return SaveUserFileJSON(scoreFileName, s)
|
||||||
|
}
|
70
score_test.go
Normal file
70
score_test.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package tins2021
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newFullHighscore() Highscores {
|
||||||
|
return Highscores{
|
||||||
|
NewScore(100, 100),
|
||||||
|
NewScore(90, 90),
|
||||||
|
NewScore(80, 80),
|
||||||
|
NewScore(70, 70),
|
||||||
|
NewScore(60, 60),
|
||||||
|
NewScore(50, 50),
|
||||||
|
NewScore(40, 40),
|
||||||
|
NewScore(30, 30),
|
||||||
|
NewScore(20, 20),
|
||||||
|
NewScore(10, 10),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddScoreBelowBottom(t *testing.T) {
|
||||||
|
h := newFullHighscore()
|
||||||
|
updated, high := h.AddScore(1, 1)
|
||||||
|
assert.False(t, high)
|
||||||
|
assert.Len(t, updated, 10)
|
||||||
|
for _, s := range h {
|
||||||
|
assert.Greater(t, s.Score, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddScoreBottom(t *testing.T) {
|
||||||
|
h := newFullHighscore()
|
||||||
|
updated, high := h.AddScore(11, 11)
|
||||||
|
assert.True(t, high)
|
||||||
|
assert.Len(t, updated, 10)
|
||||||
|
assert.Equal(t, 11, updated[9].Score)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddScoreBottomNotFull(t *testing.T) {
|
||||||
|
h := Highscores{
|
||||||
|
NewScore(100, 100),
|
||||||
|
NewScore(90, 90),
|
||||||
|
NewScore(80, 80),
|
||||||
|
NewScore(70, 70),
|
||||||
|
}
|
||||||
|
|
||||||
|
updated, high := h.AddScore(50, 50)
|
||||||
|
assert.True(t, high)
|
||||||
|
assert.Len(t, updated, 5)
|
||||||
|
assert.Equal(t, 50, updated[4].Score)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddScoreMiddle(t *testing.T) {
|
||||||
|
h := newFullHighscore()
|
||||||
|
updated, high := h.AddScore(51, 51)
|
||||||
|
assert.True(t, high)
|
||||||
|
assert.Len(t, updated, 10)
|
||||||
|
assert.Equal(t, 51, updated[5].Score)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddScoreTop(t *testing.T) {
|
||||||
|
h := newFullHighscore()
|
||||||
|
updated, high := h.AddScore(101, 101)
|
||||||
|
assert.True(t, high)
|
||||||
|
assert.Len(t, updated, 10)
|
||||||
|
assert.Equal(t, 101, updated[0].Score)
|
||||||
|
}
|
22
settings.go
22
settings.go
@ -4,35 +4,25 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
"opslag.de/schobers/zntg"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const settingsFileName = "settings.json"
|
||||||
|
|
||||||
type Settings struct {
|
type Settings struct {
|
||||||
Controls ControlsSettings
|
Controls ControlsSettings
|
||||||
Window WindowSettings
|
Window WindowSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
func SettingsPath() (string, error) {
|
|
||||||
return UserFile("settings.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Settings) Init() error {
|
func (s *Settings) Init() error {
|
||||||
path, err := SettingsPath()
|
err := LoadUserFileJSON(settingsFileName, s)
|
||||||
if err != nil {
|
if os.IsNotExist(err) {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return zntg.DecodeJSON(path, s)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) Store() error {
|
func (s *Settings) Store() error {
|
||||||
path, err := SettingsPath()
|
return SaveUserFileJSON(settingsFileName, s)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return zntg.EncodeJSON(path, s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ControlsSettings struct {
|
type ControlsSettings struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user