143 lines
2.6 KiB
Go
143 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"opslag.de/schobers/geom"
|
|
)
|
|
|
|
type entityType byte
|
|
type tile byte
|
|
|
|
const (
|
|
entityTypeInvalid entityType = entityType(0)
|
|
entityTypeNone = '_'
|
|
entityTypeCharacter = '@'
|
|
entityTypeVillain = 'X'
|
|
entityTypeBrick = 'B'
|
|
)
|
|
|
|
func (e entityType) IsValid() bool {
|
|
switch e {
|
|
case entityTypeNone:
|
|
case entityTypeCharacter:
|
|
case entityTypeVillain:
|
|
case entityTypeBrick:
|
|
default:
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
const (
|
|
tileInvalid tile = tile(0)
|
|
tileNothing = '.'
|
|
tileBasic = '#'
|
|
tileMagma = '~'
|
|
)
|
|
|
|
func (t tile) IsValid() bool {
|
|
switch t {
|
|
case tileNothing:
|
|
case tileBasic:
|
|
case tileMagma:
|
|
default:
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
type level struct {
|
|
width int
|
|
height int
|
|
tiles []tile
|
|
entities []entityType
|
|
}
|
|
|
|
func (l level) idxToPos(i int) geom.Point { return geom.Pt(i%l.width, i/l.width) }
|
|
|
|
func (l level) posToIdx(p geom.Point) int {
|
|
if p.X < 0 || p.Y < 0 || p.X >= l.width || p.Y >= l.height {
|
|
return -1
|
|
}
|
|
return p.Y*l.width + p.X
|
|
}
|
|
|
|
func parseLevelAsset(r io.Reader) (level, error) {
|
|
var l level
|
|
ctx := levelContext{&l}
|
|
err := parseLines(r, ctx.parse)
|
|
if err != nil {
|
|
return level{}, err
|
|
}
|
|
return l, nil
|
|
}
|
|
|
|
type levelContext struct {
|
|
level *level
|
|
}
|
|
|
|
func (c *levelContext) parse(p *lineParser) parseLineFn {
|
|
if p.eof() {
|
|
return nil
|
|
}
|
|
switch p.peek() {
|
|
case "level:":
|
|
return c.parseContent
|
|
case "":
|
|
p.next() // skip
|
|
return c.parse
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (c *levelContext) parseContent(p *lineParser) parseLineFn {
|
|
if p.next() != "level:" {
|
|
return p.emitErr(errors.New("expected level start"))
|
|
}
|
|
return c.parseRow
|
|
}
|
|
|
|
func (c *levelContext) parseRow(p *lineParser) parseLineFn {
|
|
if p.eof() {
|
|
return p.emitErr(errors.New("unexpected end of file"))
|
|
}
|
|
line := p.next()
|
|
if line == ":level" {
|
|
return c.parse
|
|
}
|
|
if c.level.height == 0 {
|
|
c.level.width = len(line) / 2
|
|
}
|
|
return c.addRow(p, line)
|
|
}
|
|
|
|
func (c *levelContext) addRow(p *lineParser, line string) parseLineFn {
|
|
var tiles []tile
|
|
var entities []entityType
|
|
for i := 0; i < len(line); i += 2 {
|
|
tiles = append(tiles, tile(line[i]))
|
|
entities = append(entities, entityType(line[i+1]))
|
|
}
|
|
|
|
for i, t := range tiles {
|
|
if !t.IsValid() {
|
|
return p.emitErr(fmt.Errorf("level contains invalid tile at (%d, %d)", i, c.level.height))
|
|
}
|
|
}
|
|
for i, e := range entities {
|
|
if !e.IsValid() {
|
|
return p.emitErr(fmt.Errorf("level contains invalid entity type at (%d, %d)", i, c.level.height))
|
|
}
|
|
}
|
|
|
|
c.level.height++
|
|
c.level.tiles = append(c.level.tiles, tiles...)
|
|
c.level.entities = append(c.level.entities, entities...)
|
|
|
|
return c.parseRow
|
|
}
|