Added support for level packs.
Added level selection.
This commit is contained in:
parent
5c30a4bc01
commit
a08f961e80
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ rice-box.go
|
||||
|
||||
# Application
|
||||
cmd/krampus19/res/_sandbox/
|
||||
cmd/krampus19/TODO
|
||||
|
@ -305,9 +305,9 @@ func (s *changeSettings) Enter(ctx *Context) error {
|
||||
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))
|
||||
return nil
|
||||
|
@ -24,8 +24,8 @@ type Context struct {
|
||||
DisplaySize geom.Point
|
||||
Resources vfs.CopyDir
|
||||
Textures map[string]texture
|
||||
Levels map[string]level
|
||||
Sprites map[string]sprite
|
||||
Levels map[string]levelPack
|
||||
SpriteDrawer SpriteDrawer
|
||||
Settings settings
|
||||
Palette *alui.Palette
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"opslag.de/schobers/allg5"
|
||||
@ -89,20 +90,23 @@ func (g *game) loadTextures(pathToName map[string]string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *game) loadLevels(names ...string) error {
|
||||
g.ctx.Levels = map[string]level{}
|
||||
for _, name := range names {
|
||||
fileName := fmt.Sprintf("levels/level%s.txt", name)
|
||||
func (g *game) loadLevelPack(ids ...string) error {
|
||||
g.ctx.Levels = map[string]levelPack{}
|
||||
for _, id := range ids {
|
||||
fileName := fmt.Sprintf("levels/pack%s.txt", id)
|
||||
f, err := g.ctx.Resources.Open(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
g.ctx.Levels[name] = level
|
||||
g.ctx.Levels[id] = pack
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -151,7 +155,7 @@ func (g *game) loadAssets() error {
|
||||
log.Printf("Loaded %d textures.\n", len(g.ctx.Textures))
|
||||
|
||||
log.Println("Loading levels...")
|
||||
err = g.loadLevels("1", "2")
|
||||
err = g.loadLevelPack("1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -192,7 +196,7 @@ func (g *game) Init(disp *allg5.Display, settings settings, res vfs.CopyDir, con
|
||||
}
|
||||
log.Print("Loaded assets.")
|
||||
|
||||
g.ctx.Navigation.showMainMenu()
|
||||
g.ctx.Navigation.ShowMainMenu()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ func (l level) posToIdx(p geom.Point) int {
|
||||
return p.Y*l.width + p.X
|
||||
}
|
||||
|
||||
func loadLevelAsset(r io.Reader) (level, error) {
|
||||
func parseLevelAsset(r io.Reader) (level, error) {
|
||||
var l level
|
||||
ctx := levelContext{&l}
|
||||
err := parseLines(r, ctx.parse)
|
||||
|
106
cmd/krampus19/levelpack.go
Normal file
106
cmd/krampus19/levelpack.go
Normal 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"))
|
||||
}
|
34
cmd/krampus19/levelselect.go
Normal file
34
cmd/krampus19/levelselect.go
Normal 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() {}
|
@ -35,6 +35,15 @@ func (p *lineParser) skipSpaceEOF() bool {
|
||||
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
|
||||
|
||||
func parseLines(r io.Reader, fn parseLineFn) error {
|
||||
|
@ -13,9 +13,9 @@ type mainMenu struct {
|
||||
func (m *mainMenu) Enter(ctx *Context) error {
|
||||
m.ctx = ctx
|
||||
m.Init()
|
||||
m.Add("Play", func() { m.ctx.Navigation.playLevel("2") })
|
||||
m.Add("Settings", func() { m.ctx.Navigation.changeSettings() })
|
||||
m.Add("Quit", func() { m.ctx.Navigation.quit() })
|
||||
m.Add("Play", func() { m.ctx.Navigation.SelectLevel("1") })
|
||||
m.Add("Settings", func() { m.ctx.Navigation.ChangeSettings() })
|
||||
m.Add("Quit", func() { m.ctx.Navigation.Quit() })
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -16,22 +16,26 @@ type scene interface {
|
||||
Leave()
|
||||
}
|
||||
|
||||
func (n *navigation) changeSettings() {
|
||||
func (n *navigation) ChangeSettings() {
|
||||
n.switchTo(&changeSettings{})
|
||||
}
|
||||
|
||||
func (n *navigation) playLevel(l string) {
|
||||
n.switchTo(&playLevel{name: l})
|
||||
func (n *navigation) PlayLevel(packID, levelID string) {
|
||||
n.switchTo(&playLevel{packID: packID, levelID: levelID})
|
||||
}
|
||||
|
||||
func (n *navigation) quit() {
|
||||
n.switchTo(nil)
|
||||
func (n *navigation) SelectLevel(packID string) {
|
||||
n.switchTo(&levelSelect{packID: packID})
|
||||
}
|
||||
|
||||
func (n *navigation) showMainMenu() {
|
||||
func (n *navigation) ShowMainMenu() {
|
||||
n.switchTo(&mainMenu{})
|
||||
}
|
||||
|
||||
func (n *navigation) Quit() {
|
||||
n.switchTo(nil)
|
||||
}
|
||||
|
||||
func (n *navigation) switchTo(s scene) {
|
||||
if n.curr != nil {
|
||||
n.curr.Leave()
|
||||
|
@ -19,7 +19,8 @@ type playLevel struct {
|
||||
menu *alui.Menu
|
||||
showMenu bool
|
||||
|
||||
name string
|
||||
packID string
|
||||
levelID string
|
||||
offset geom.PointF32
|
||||
scale float32
|
||||
|
||||
@ -80,13 +81,14 @@ func (l *playLevel) Enter(ctx *Context) error {
|
||||
|
||||
l.menu = alui.NewMenu()
|
||||
l.menu.Add("Resume", func() { l.showMenu = false })
|
||||
l.menu.Add("Restart", func() { l.ctx.Navigation.playLevel(l.name) })
|
||||
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.Add("Restart", func() { l.ctx.Navigation.PlayLevel(l.packID, l.levelID) })
|
||||
l.menu.Add("Select other level", func() { l.ctx.Navigation.SelectLevel(l.packID) })
|
||||
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.init = true
|
||||
l.state.Init(l.ctx, l.name)
|
||||
l.state.Init(l.ctx, l.packID, l.levelID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ func findEntityIdx(entities []*entity, pos geom.Point) int {
|
||||
type playLevelState struct {
|
||||
ctx *Context
|
||||
|
||||
pack levelPack
|
||||
level level
|
||||
player *entity
|
||||
villain *entity
|
||||
@ -61,9 +62,10 @@ func (s *playLevelState) IsNextToMagma(pos geom.Point) bool {
|
||||
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.level = ctx.Levels[levelName]
|
||||
s.pack = ctx.Levels[pack]
|
||||
s.level = s.pack.levels[level]
|
||||
s.bricks = nil
|
||||
s.sunken = nil
|
||||
for i, e := range s.level.entities {
|
||||
|
13
cmd/krampus19/res/levels/pack1.txt
Normal file
13
cmd/krampus19/res/levels/pack1.txt
Normal file
@ -0,0 +1,13 @@
|
||||
name: Standard
|
||||
|
||||
levels:
|
||||
|
||||
level:
|
||||
id: 1
|
||||
:level
|
||||
|
||||
level:
|
||||
id: 2
|
||||
:level
|
||||
|
||||
:levels
|
Loading…
Reference in New Issue
Block a user