Compare commits
No commits in common. "99d9d09c2ff7a9da7d41d9c69dde10fc20e6875d" and "c52f2682e05990edee5cd3e95a09c48b3a020297" have entirely different histories.
99d9d09c2f
...
c52f2682e0
2
TODO.md
2
TODO.md
@ -1,6 +1,6 @@
|
|||||||
- [X] Increase difficulty.
|
- [X] Increase difficulty.
|
||||||
- [ ] Add music & sounds.
|
- [ ] Add music & sounds.
|
||||||
- [X] Keep score/difficulty level (resume & restart).
|
- [ ] 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,7 +10,6 @@ type app struct {
|
|||||||
ui.Proxy
|
ui.Proxy
|
||||||
|
|
||||||
settings *tins2021.Settings
|
settings *tins2021.Settings
|
||||||
score *tins2021.ScoreState
|
|
||||||
|
|
||||||
context *appContext
|
context *appContext
|
||||||
}
|
}
|
||||||
@ -56,7 +55,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, a.score, func(control ui.Control) {
|
a.context = newAppContext(ctx, a.settings, func(control ui.Control) {
|
||||||
a.Content = control
|
a.Content = control
|
||||||
})
|
})
|
||||||
a.context.ShowMainMenu(ctx)
|
a.context.ShowMainMenu(ctx)
|
||||||
|
@ -9,7 +9,6 @@ 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
|
||||||
@ -18,12 +17,11 @@ type appContext struct {
|
|||||||
MonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture
|
MonsterTextures map[tins2021.MonsterType]tins2021.AnimatedTexture
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAppContext(ctx ui.Context, settings *tins2021.Settings, score *tins2021.ScoreState, setView func(ui.Control)) *appContext {
|
func newAppContext(ctx ui.Context, settings *tins2021.Settings, 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{
|
||||||
@ -42,7 +40,6 @@ 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))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,14 +56,6 @@ 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)
|
||||||
}
|
}
|
||||||
@ -79,10 +68,6 @@ 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))
|
||||||
}
|
}
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
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,8 +30,6 @@ 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 {
|
||||||
@ -86,23 +84,11 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,7 +102,6 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,9 +113,6 @@ 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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,9 +131,6 @@ 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
|
||||||
@ -349,12 +328,6 @@ 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,10 +37,6 @@ 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() })
|
||||||
|
@ -86,7 +86,7 @@ func generateArrowKeysHighlight(resources ui.Resources, highlight [4]bool) image
|
|||||||
}
|
}
|
||||||
|
|
||||||
return generateKeys(resources,
|
return generateKeys(resources,
|
||||||
keyboardLayoutKey{Position: geom.PtF(.53, .25), Key: spaceOrRune('↑', !highlight[0]), Highlight: highlight[0]},
|
keyboardLayoutKey{Position: geom.PtF(.45, .25), Key: spaceOrRune('↑', !highlight[0]), Highlight: highlight[0]},
|
||||||
keyboardLayoutKey{Position: geom.PtF(.2, .75), Key: spaceOrRune('←', !highlight[1]), Highlight: highlight[1]},
|
keyboardLayoutKey{Position: geom.PtF(.2, .75), Key: spaceOrRune('←', !highlight[1]), Highlight: highlight[1]},
|
||||||
keyboardLayoutKey{Position: geom.PtF(.5, .75), Key: spaceOrRune('↓', !highlight[2]), Highlight: highlight[2]},
|
keyboardLayoutKey{Position: geom.PtF(.5, .75), Key: spaceOrRune('↓', !highlight[2]), Highlight: highlight[2]},
|
||||||
keyboardLayoutKey{Position: geom.PtF(.8, .75), Key: spaceOrRune('→', !highlight[3]), Highlight: highlight[3]},
|
keyboardLayoutKey{Position: geom.PtF(.8, .75), Key: spaceOrRune('→', !highlight[3]), Highlight: highlight[3]},
|
||||||
@ -315,7 +315,7 @@ func (s *settings) Render(ctx ui.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.SelectingCustom > 0 {
|
if s.SelectingCustom > 0 {
|
||||||
renderer.FillRectangle(bounds, zntg.MustHexColor(`#000000DF`))
|
renderer.FillRectangle(bounds, zntg.MustHexColor(`#000000CF`))
|
||||||
|
|
||||||
selectTexture := fmt.Sprintf("layout-select-%d", s.SelectingCustom)
|
selectTexture := fmt.Sprintf("layout-select-%d", s.SelectingCustom)
|
||||||
|
|
||||||
|
@ -50,11 +50,6 @@ 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)}
|
||||||
@ -79,10 +74,7 @@ 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,43 +1,11 @@
|
|||||||
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.UserConfigDir(appName) }
|
func UserDir() (string, error) { return zntg.UserDir(appName) }
|
||||||
|
|
||||||
func UserFile(name string) (string, error) { return zntg.UserConfigFile(appName, name) }
|
func UserFile(name string) (string, error) { return zntg.UserFile(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
83
score.go
@ -1,83 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
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,25 +4,35 @@ 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 {
|
||||||
err := LoadUserFileJSON(settingsFileName, s)
|
path, err := SettingsPath()
|
||||||
if os.IsNotExist(err) {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return zntg.DecodeJSON(path, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) Store() error {
|
func (s *Settings) Store() error {
|
||||||
return SaveUserFileJSON(settingsFileName, s)
|
path, err := SettingsPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return zntg.EncodeJSON(path, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ControlsSettings struct {
|
type ControlsSettings struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user