From a08f961e800952a00a719895058e640bce528c4e Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Sat, 28 Dec 2019 21:20:56 +0100 Subject: [PATCH] Added support for level packs. Added level selection. --- .gitignore | 1 + cmd/krampus19/changesettings.go | 4 +- cmd/krampus19/context.go | 2 +- cmd/krampus19/game.go | 20 ++-- cmd/krampus19/level.go | 2 +- cmd/krampus19/levelpack.go | 106 ++++++++++++++++++ cmd/krampus19/levelselect.go | 34 ++++++ cmd/krampus19/lineparser.go | 9 ++ cmd/krampus19/mainmenu.go | 6 +- cmd/krampus19/navigation.go | 16 ++- cmd/krampus19/playlevel.go | 16 +-- cmd/krampus19/playlevelstate.go | 6 +- cmd/krampus19/res/levels/pack1.txt | 13 +++ .../levels/{level1.txt => pack1_level1.txt} | 0 .../levels/{level2.txt => pack1_level2.txt} | 0 15 files changed, 205 insertions(+), 30 deletions(-) create mode 100644 cmd/krampus19/levelpack.go create mode 100644 cmd/krampus19/levelselect.go create mode 100644 cmd/krampus19/res/levels/pack1.txt rename cmd/krampus19/res/levels/{level1.txt => pack1_level1.txt} (100%) rename cmd/krampus19/res/levels/{level2.txt => pack1_level2.txt} (100%) diff --git a/.gitignore b/.gitignore index daabb01..f9e8952 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ rice-box.go # Application cmd/krampus19/res/_sandbox/ +cmd/krampus19/TODO diff --git a/cmd/krampus19/changesettings.go b/cmd/krampus19/changesettings.go index d1cb8f7..2944a18 100644 --- a/cmd/krampus19/changesettings.go +++ b/cmd/krampus19/changesettings.go @@ -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 diff --git a/cmd/krampus19/context.go b/cmd/krampus19/context.go index b10206a..ae32c40 100644 --- a/cmd/krampus19/context.go +++ b/cmd/krampus19/context.go @@ -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 diff --git a/cmd/krampus19/game.go b/cmd/krampus19/game.go index 1836fc7..9cfc129 100644 --- a/cmd/krampus19/game.go +++ b/cmd/krampus19/game.go @@ -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 } diff --git a/cmd/krampus19/level.go b/cmd/krampus19/level.go index 46512e7..928b814 100644 --- a/cmd/krampus19/level.go +++ b/cmd/krampus19/level.go @@ -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) diff --git a/cmd/krampus19/levelpack.go b/cmd/krampus19/levelpack.go new file mode 100644 index 0000000..0f19044 --- /dev/null +++ b/cmd/krampus19/levelpack.go @@ -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")) +} diff --git a/cmd/krampus19/levelselect.go b/cmd/krampus19/levelselect.go new file mode 100644 index 0000000..08e7055 --- /dev/null +++ b/cmd/krampus19/levelselect.go @@ -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() {} diff --git a/cmd/krampus19/lineparser.go b/cmd/krampus19/lineparser.go index 7f90769..da0db98 100644 --- a/cmd/krampus19/lineparser.go +++ b/cmd/krampus19/lineparser.go @@ -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 { diff --git a/cmd/krampus19/mainmenu.go b/cmd/krampus19/mainmenu.go index 6b8881d..17abd51 100644 --- a/cmd/krampus19/mainmenu.go +++ b/cmd/krampus19/mainmenu.go @@ -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 } diff --git a/cmd/krampus19/navigation.go b/cmd/krampus19/navigation.go index bb5ee6c..fc240f9 100644 --- a/cmd/krampus19/navigation.go +++ b/cmd/krampus19/navigation.go @@ -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() diff --git a/cmd/krampus19/playlevel.go b/cmd/krampus19/playlevel.go index a9ec37a..ef4ee57 100644 --- a/cmd/krampus19/playlevel.go +++ b/cmd/krampus19/playlevel.go @@ -19,9 +19,10 @@ type playLevel struct { menu *alui.Menu showMenu bool - name string - offset geom.PointF32 - scale float32 + packID string + levelID string + offset geom.PointF32 + scale float32 state playLevelState } @@ -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 } diff --git a/cmd/krampus19/playlevelstate.go b/cmd/krampus19/playlevelstate.go index 97b0c1b..10e3f81 100644 --- a/cmd/krampus19/playlevelstate.go +++ b/cmd/krampus19/playlevelstate.go @@ -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 { diff --git a/cmd/krampus19/res/levels/pack1.txt b/cmd/krampus19/res/levels/pack1.txt new file mode 100644 index 0000000..9783ea4 --- /dev/null +++ b/cmd/krampus19/res/levels/pack1.txt @@ -0,0 +1,13 @@ +name: Standard + +levels: + +level: +id: 1 +:level + +level: +id: 2 +:level + +:levels \ No newline at end of file diff --git a/cmd/krampus19/res/levels/level1.txt b/cmd/krampus19/res/levels/pack1_level1.txt similarity index 100% rename from cmd/krampus19/res/levels/level1.txt rename to cmd/krampus19/res/levels/pack1_level1.txt diff --git a/cmd/krampus19/res/levels/level2.txt b/cmd/krampus19/res/levels/pack1_level2.txt similarity index 100% rename from cmd/krampus19/res/levels/level2.txt rename to cmd/krampus19/res/levels/pack1_level2.txt