Added support for level packs.

Added level selection.
This commit is contained in:
Sander Schobers 2019-12-28 21:20:56 +01:00
parent 5c30a4bc01
commit a08f961e80
15 changed files with 205 additions and 30 deletions

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ rice-box.go
# Application # Application
cmd/krampus19/res/_sandbox/ cmd/krampus19/res/_sandbox/
cmd/krampus19/TODO

View File

@ -305,9 +305,9 @@ func (s *changeSettings) Enter(ctx *Context) error {
log.Printf("Stored new settings.") log.Printf("Stored new settings.")
} }
s.ctx.Navigation.showMainMenu() s.ctx.Navigation.ShowMainMenu()
}), }),
newWideButton("Cancel", func() { s.ctx.Navigation.showMainMenu() }), newWideButton("Cancel", func() { s.ctx.Navigation.ShowMainMenu() }),
) )
s.AddChild(alui.Center(buttons)) s.AddChild(alui.Center(buttons))
return nil return nil

View File

@ -24,8 +24,8 @@ type Context struct {
DisplaySize geom.Point DisplaySize geom.Point
Resources vfs.CopyDir Resources vfs.CopyDir
Textures map[string]texture Textures map[string]texture
Levels map[string]level
Sprites map[string]sprite Sprites map[string]sprite
Levels map[string]levelPack
SpriteDrawer SpriteDrawer SpriteDrawer SpriteDrawer
Settings settings Settings settings
Palette *alui.Palette Palette *alui.Palette

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"image/png" "image/png"
"io"
"log" "log"
"opslag.de/schobers/allg5" "opslag.de/schobers/allg5"
@ -89,20 +90,23 @@ func (g *game) loadTextures(pathToName map[string]string) error {
return nil return nil
} }
func (g *game) loadLevels(names ...string) error { func (g *game) loadLevelPack(ids ...string) error {
g.ctx.Levels = map[string]level{} g.ctx.Levels = map[string]levelPack{}
for _, name := range names { for _, id := range ids {
fileName := fmt.Sprintf("levels/level%s.txt", name) fileName := fmt.Sprintf("levels/pack%s.txt", id)
f, err := g.ctx.Resources.Open(fileName) f, err := g.ctx.Resources.Open(fileName)
if err != nil { if err != nil {
return err return err
} }
defer f.Close() defer f.Close()
level, err := loadLevelAsset(f) pack, err := parseLevelPackAsset(f, func(levelID string) (io.ReadCloser, error) {
fileName := fmt.Sprintf("levels/pack%s_level%s.txt", id, levelID)
return g.ctx.Resources.Open(fileName)
})
if err != nil { if err != nil {
return err return err
} }
g.ctx.Levels[name] = level g.ctx.Levels[id] = pack
} }
return nil return nil
} }
@ -151,7 +155,7 @@ func (g *game) loadAssets() error {
log.Printf("Loaded %d textures.\n", len(g.ctx.Textures)) log.Printf("Loaded %d textures.\n", len(g.ctx.Textures))
log.Println("Loading levels...") log.Println("Loading levels...")
err = g.loadLevels("1", "2") err = g.loadLevelPack("1")
if err != nil { if err != nil {
return err return err
} }
@ -192,7 +196,7 @@ func (g *game) Init(disp *allg5.Display, settings settings, res vfs.CopyDir, con
} }
log.Print("Loaded assets.") log.Print("Loaded assets.")
g.ctx.Navigation.showMainMenu() g.ctx.Navigation.ShowMainMenu()
return nil return nil
} }

View File

@ -65,7 +65,7 @@ func (l level) posToIdx(p geom.Point) int {
return p.Y*l.width + p.X return p.Y*l.width + p.X
} }
func loadLevelAsset(r io.Reader) (level, error) { func parseLevelAsset(r io.Reader) (level, error) {
var l level var l level
ctx := levelContext{&l} ctx := levelContext{&l}
err := parseLines(r, ctx.parse) err := parseLines(r, ctx.parse)

106
cmd/krampus19/levelpack.go Normal file
View File

@ -0,0 +1,106 @@
package main
import (
"errors"
"io"
"strings"
)
type levelPack struct {
name string
order []string
levels map[string]level
}
type parseLevelPackContext struct {
name string
levels []string
}
func parseLevelPackAsset(r io.Reader, openLevelFn func(id string) (io.ReadCloser, error)) (levelPack, error) {
ctx := &parseLevelPackContext{}
err := parseLines(r, ctx.Parse)
if err != nil {
return levelPack{}, err
}
pack := levelPack{name: ctx.name, levels: map[string]level{}}
for _, id := range ctx.levels {
rc, err := openLevelFn(id)
if err != nil {
return levelPack{}, err
}
defer rc.Close()
level, err := parseLevelAsset(rc)
if err != nil {
return levelPack{}, err
}
pack.order = append(pack.order, id)
pack.levels[id] = level
}
return pack, nil
}
func (c *parseLevelPackContext) Parse(p *lineParser) parseLineFn {
if p.skipSpaceEOF() {
return p.emitErr(errors.New("empty level pack"))
}
return c.parse
}
func (c *parseLevelPackContext) parse(p *lineParser) parseLineFn {
const levelsTag = "levels:"
const nameTag = "name:"
if p.skipSpaceEOF() {
return nil
}
line := p.next()
switch {
case strings.HasPrefix(line, nameTag):
c.name = strings.TrimSpace(line[len(nameTag):])
return c.parse
case strings.HasPrefix(line, levelsTag):
return skipSpaceBeforeContent(c.parseLevels)
}
return p.emitErr(errors.New("tag not allowed"))
}
func (c *parseLevelPackContext) parseLevels(p *lineParser) parseLineFn {
const levelTag = "level:"
const levelsEndTag = ":levels"
line := p.next()
switch line {
case levelTag:
return skipSpaceBeforeContent(c.parseLevel)
case levelsEndTag:
return c.parse
}
return p.emitErr(errors.New("tag not allowed"))
}
func (c *parseLevelPackContext) parseLevel(p *lineParser) parseLineFn {
const idTag = "id:"
line := p.next()
switch {
case strings.HasPrefix(line, idTag):
c.levels = append(c.levels, strings.TrimSpace(line[len(idTag):]))
return skipSpaceBeforeContent(c.parseLevelEnd)
}
return p.emitErr(errors.New("must have an id tag"))
}
func (c *parseLevelPackContext) parseLevelEnd(p *lineParser) parseLineFn {
const levelEndTag = ":level"
line := p.next()
switch {
case strings.HasPrefix(line, levelEndTag):
return skipSpaceBeforeContent(c.parseLevels)
}
return p.emitErr(errors.New("tag not allowed"))
}

View File

@ -0,0 +1,34 @@
package main
import (
"fmt"
"opslag.de/schobers/krampus19/alui"
)
type levelSelect struct {
alui.Menu
ctx *Context
packID string
pack levelPack
}
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) }
for _, id := range s.pack.order {
// level := s.pack[id]
levelID := id
s.Add(name(levelID), func() {
s.ctx.Navigation.PlayLevel(s.packID, levelID)
})
}
s.Add("Back to main menu", func() { s.ctx.Navigation.ShowMainMenu() })
return nil
}
func (s *levelSelect) Leave() {}

View File

@ -35,6 +35,15 @@ func (p *lineParser) skipSpaceEOF() bool {
return p.eof() return p.eof()
} }
func skipSpaceBeforeContent(next parseLineFn) parseLineFn {
return func(p *lineParser) parseLineFn {
if p.skipSpaceEOF() {
return p.emitErr(errUnexpectedEnd)
}
return next
}
}
type parseLineFn func(p *lineParser) parseLineFn type parseLineFn func(p *lineParser) parseLineFn
func parseLines(r io.Reader, fn parseLineFn) error { func parseLines(r io.Reader, fn parseLineFn) error {

View File

@ -13,9 +13,9 @@ type mainMenu struct {
func (m *mainMenu) Enter(ctx *Context) error { func (m *mainMenu) Enter(ctx *Context) error {
m.ctx = ctx m.ctx = ctx
m.Init() m.Init()
m.Add("Play", func() { m.ctx.Navigation.playLevel("2") }) m.Add("Play", func() { m.ctx.Navigation.SelectLevel("1") })
m.Add("Settings", func() { m.ctx.Navigation.changeSettings() }) m.Add("Settings", func() { m.ctx.Navigation.ChangeSettings() })
m.Add("Quit", func() { m.ctx.Navigation.quit() }) m.Add("Quit", func() { m.ctx.Navigation.Quit() })
return nil return nil
} }

View File

@ -16,22 +16,26 @@ type scene interface {
Leave() Leave()
} }
func (n *navigation) changeSettings() { func (n *navigation) ChangeSettings() {
n.switchTo(&changeSettings{}) n.switchTo(&changeSettings{})
} }
func (n *navigation) playLevel(l string) { func (n *navigation) PlayLevel(packID, levelID string) {
n.switchTo(&playLevel{name: l}) n.switchTo(&playLevel{packID: packID, levelID: levelID})
} }
func (n *navigation) quit() { func (n *navigation) SelectLevel(packID string) {
n.switchTo(nil) n.switchTo(&levelSelect{packID: packID})
} }
func (n *navigation) showMainMenu() { func (n *navigation) ShowMainMenu() {
n.switchTo(&mainMenu{}) n.switchTo(&mainMenu{})
} }
func (n *navigation) Quit() {
n.switchTo(nil)
}
func (n *navigation) switchTo(s scene) { func (n *navigation) switchTo(s scene) {
if n.curr != nil { if n.curr != nil {
n.curr.Leave() n.curr.Leave()

View File

@ -19,9 +19,10 @@ type playLevel struct {
menu *alui.Menu menu *alui.Menu
showMenu bool showMenu bool
name string packID string
offset geom.PointF32 levelID string
scale float32 offset geom.PointF32
scale float32
state playLevelState state playLevelState
} }
@ -80,13 +81,14 @@ func (l *playLevel) Enter(ctx *Context) error {
l.menu = alui.NewMenu() l.menu = alui.NewMenu()
l.menu.Add("Resume", func() { l.showMenu = false }) l.menu.Add("Resume", func() { l.showMenu = false })
l.menu.Add("Restart", func() { l.ctx.Navigation.playLevel(l.name) }) l.menu.Add("Restart", func() { l.ctx.Navigation.PlayLevel(l.packID, l.levelID) })
l.menu.Add("Quit to main menu", func() { l.ctx.Navigation.showMainMenu() }) l.menu.Add("Select other level", func() { l.ctx.Navigation.SelectLevel(l.packID) })
l.menu.Add("Quit to desktop", func() { l.ctx.Navigation.quit() }) l.menu.Add("Quit to main menu", func() { l.ctx.Navigation.ShowMainMenu() })
l.menu.Add("Quit to desktop", func() { l.ctx.Navigation.Quit() })
l.menu.OnEscape = func() { l.showMenu = false } l.menu.OnEscape = func() { l.showMenu = false }
l.init = true l.init = true
l.state.Init(l.ctx, l.name) l.state.Init(l.ctx, l.packID, l.levelID)
return nil return nil
} }

View File

@ -29,6 +29,7 @@ func findEntityIdx(entities []*entity, pos geom.Point) int {
type playLevelState struct { type playLevelState struct {
ctx *Context ctx *Context
pack levelPack
level level level level
player *entity player *entity
villain *entity villain *entity
@ -61,9 +62,10 @@ func (s *playLevelState) IsNextToMagma(pos geom.Point) bool {
s.checkTile(pos.Add2D(0, 1), s.isMagma) s.checkTile(pos.Add2D(0, 1), s.isMagma)
} }
func (s *playLevelState) Init(ctx *Context, levelName string) { func (s *playLevelState) Init(ctx *Context, pack, level string) {
s.ctx = ctx s.ctx = ctx
s.level = ctx.Levels[levelName] s.pack = ctx.Levels[pack]
s.level = s.pack.levels[level]
s.bricks = nil s.bricks = nil
s.sunken = nil s.sunken = nil
for i, e := range s.level.entities { for i, e := range s.level.entities {

View File

@ -0,0 +1,13 @@
name: Standard
levels:
level:
id: 1
:level
level:
id: 2
:level
:levels