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
|
# Application
|
||||||
cmd/krampus19/res/_sandbox/
|
cmd/krampus19/res/_sandbox/
|
||||||
|
cmd/krampus19/TODO
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
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()
|
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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
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