Added sprites & tried out splash animation.
Moveds fonts to separate directory & added corresponding license texts. Moved console & fps to separte gut (game utility) package. Added console (view only).
This commit is contained in:
parent
afaa190648
commit
b85ac17d8a
74
cmd/krampus19/console.go
Normal file
74
cmd/krampus19/console.go
Normal file
@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"opslag.de/schobers/allg5"
|
||||
"opslag.de/schobers/geom"
|
||||
"opslag.de/schobers/krampus19/alui"
|
||||
"opslag.de/schobers/krampus19/gut"
|
||||
)
|
||||
|
||||
type console struct {
|
||||
alui.ControlBase
|
||||
|
||||
cons *gut.Console
|
||||
|
||||
buffer *allg5.Bitmap
|
||||
bufferN int
|
||||
height float32
|
||||
offset float32
|
||||
}
|
||||
|
||||
func newConsole(cons *gut.Console) *console {
|
||||
return &console{cons: cons, height: 300}
|
||||
}
|
||||
|
||||
func (c *console) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
back := allg5.NewColorAlpha(0, 0, 0, 0x7f)
|
||||
line := allg5.NewColor(0x7f, 0x7f, 0x7f)
|
||||
fore := allg5.NewColor(0xff, 0xff, 0xff)
|
||||
|
||||
c.height = geom.Min32(c.height, bounds.Dy())
|
||||
top := geom.Max32(bounds.Max.Y-c.height, bounds.Min.Y)
|
||||
allg5.DrawFilledRectangle(bounds.Min.X, top, bounds.Max.X, bounds.Max.Y, back)
|
||||
allg5.DrawLine(bounds.Min.X, top, bounds.Max.X, top, line, 1)
|
||||
|
||||
font := ctx.Fonts.Get("console")
|
||||
lineHeight := font.Height() * 1.1
|
||||
|
||||
size := geom.Pt(int(bounds.Dx()-8), int(c.height-8))
|
||||
if c.buffer == nil || c.buffer.Height() != size.Y || c.buffer.Width() != size.X {
|
||||
if c.buffer != nil {
|
||||
c.buffer.Destroy()
|
||||
}
|
||||
buffer, err := allg5.NewVideoBitmap(size.X, size.Y)
|
||||
if err != nil {
|
||||
log.Fatal("Couldn't create new video bitmap")
|
||||
}
|
||||
c.buffer = buffer
|
||||
c.bufferN = -1
|
||||
}
|
||||
|
||||
messages := c.cons.Messages()
|
||||
messagesN := len(messages)
|
||||
if messagesN != c.bufferN {
|
||||
c.buffer.SetAsTarget()
|
||||
|
||||
size := geom.PtF32(float32(size.X), float32(size.Y))
|
||||
totalHeight := lineHeight * float32(messagesN)
|
||||
if totalHeight < size.Y {
|
||||
c.offset = size.Y - totalHeight
|
||||
}
|
||||
messageTop := size.Y - totalHeight - c.offset
|
||||
for _, m := range messages {
|
||||
if messageTop <= size.Y || (messageTop+lineHeight) >= 0 {
|
||||
font.Draw(0, messageTop, fore, allg5.AlignLeft, m)
|
||||
}
|
||||
messageTop += lineHeight
|
||||
}
|
||||
c.bufferN = messagesN
|
||||
ctx.Display.SetAsTarget()
|
||||
}
|
||||
c.buffer.Draw(bounds.Min.X+4, bounds.Max.Y-c.height+4)
|
||||
}
|
@ -1,18 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"opslag.de/schobers/allg5"
|
||||
"opslag.de/schobers/fs/vfs"
|
||||
)
|
||||
|
||||
type Texture struct {
|
||||
*allg5.Bitmap
|
||||
|
||||
Subs map[string]*allg5.Bitmap
|
||||
}
|
||||
|
||||
func NewTexture(bmp *allg5.Bitmap) Texture {
|
||||
return Texture{Bitmap: bmp, Subs: map[string]*allg5.Bitmap{}}
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
Resources vfs.CopyDir
|
||||
Bitmaps map[string]*allg5.Bitmap
|
||||
Textures map[string]Texture
|
||||
Levels map[string]level
|
||||
Sprites map[string]sprite
|
||||
|
||||
Tick time.Duration
|
||||
}
|
||||
|
||||
func (c *Context) Destroy() {
|
||||
for _, bmp := range c.Bitmaps {
|
||||
for _, bmp := range c.Textures {
|
||||
bmp.Destroy()
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"log"
|
||||
|
||||
"opslag.de/schobers/allg5"
|
||||
"opslag.de/schobers/fs/vfs"
|
||||
"opslag.de/schobers/krampus19/alui"
|
||||
"opslag.de/schobers/krampus19/gut"
|
||||
)
|
||||
|
||||
type Game struct {
|
||||
@ -15,24 +18,18 @@ type Game struct {
|
||||
main alui.Container
|
||||
|
||||
info *alui.Overlay
|
||||
cons *alui.Overlay
|
||||
scene *alui.Overlay
|
||||
}
|
||||
|
||||
func (g *Game) initUI(disp *allg5.Display, fps *FPS) error {
|
||||
func (g *Game) initUI(disp *allg5.Display, cons *gut.Console, fps *gut.FPS) error {
|
||||
disp.SetWindowTitle("Krampushack '19 - Title TBD - by Tharro")
|
||||
ui := alui.NewUI(disp, &g.main)
|
||||
fontPath, err := g.ctx.Resources.Retrieve("OpenSans-Regular.ttf")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ui.Fonts().LoadFonts(alui.FontDescription{Path: fontPath, Name: "default", Size: 12})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.ui = ui
|
||||
g.info = &alui.Overlay{Proxy: &info{fps: fps}}
|
||||
g.scene = &alui.Overlay{Proxy: &playScene{}, Visible: true}
|
||||
g.main.Children = append(g.main.Children, g.scene, g.info)
|
||||
g.cons = &alui.Overlay{Proxy: newConsole(cons)}
|
||||
g.scene = &alui.Overlay{Proxy: &splash{}, Visible: true}
|
||||
g.main.Children = append(g.main.Children, g.scene, &alui.Container{Children: []alui.Control{g.info, g.cons}})
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -50,11 +47,31 @@ func (g *Game) loadBitmap(path, name string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.ctx.Bitmaps[name] = bmp
|
||||
g.ctx.Textures[name] = NewTexture(bmp)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) loadBitmaps(pathToName map[string]string) error {
|
||||
func (g *Game) loadFonts() error {
|
||||
openSansPath, err := g.ctx.Resources.Retrieve("fonts/OpenSans-Regular.ttf")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
firaMonoPath, err := g.ctx.Resources.Retrieve("fonts/FiraMono-Regular.ttf")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = g.ui.Fonts().LoadFonts(alui.FontDescription{Path: openSansPath, Name: "default", Size: 12})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = g.ui.Fonts().LoadFonts(alui.FontDescription{Path: firaMonoPath, Name: "console", Size: 12})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) loadTextures(pathToName map[string]string) error {
|
||||
for path, name := range pathToName {
|
||||
err := g.loadBitmap(path, name)
|
||||
if err != nil {
|
||||
@ -82,11 +99,40 @@ func (g *Game) loadLevels(names ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) loadSprites(names ...string) error {
|
||||
g.ctx.Sprites = map[string]sprite{}
|
||||
for _, name := range names {
|
||||
fileName := fmt.Sprintf("sprites/%s.txt", name)
|
||||
f, err := g.ctx.Resources.Open(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
sprite, err := loadSpriteAsset(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
texture, ok := g.ctx.Textures[sprite.texture]
|
||||
if !ok {
|
||||
return errors.New("sprite texture not found")
|
||||
}
|
||||
for _, part := range sprite.parts {
|
||||
sub := part.sub
|
||||
texture.Subs[part.name] = texture.Sub(sub.Min.X, sub.Min.Y, sub.Max.X-sub.Min.X, sub.Max.Y-sub.Min.Y)
|
||||
}
|
||||
g.ctx.Sprites[name] = sprite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) loadAssets() error {
|
||||
err := g.loadBitmaps(map[string]string{
|
||||
log.Println("Loading textures...")
|
||||
err := g.loadTextures(map[string]string{
|
||||
"basic_tile.png": "basic_tile",
|
||||
"water_tile.png": "water_tile",
|
||||
|
||||
// "dragon.png": "dragon",
|
||||
"main_character.png": "main_character",
|
||||
"villain_character.png": "villain_character",
|
||||
|
||||
@ -96,16 +142,28 @@ func (g *Game) loadAssets() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("Loaded %d textures.\n", len(g.ctx.Textures))
|
||||
|
||||
log.Println("Loading levels...")
|
||||
err = g.loadLevels("1")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
log.Printf("Loaded %d levels.\n", len(g.ctx.Levels))
|
||||
|
||||
func (g *Game) PlayScene() *playScene {
|
||||
return g.scene.Proxy.(*playScene)
|
||||
log.Println("Loading fonts...")
|
||||
err = g.loadFonts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("Loading sprites")
|
||||
// err = g.loadSprites("dragon")
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
log.Printf("Loaded %d sprites.\n", len(g.ctx.Sprites))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) Destroy() {
|
||||
@ -113,17 +171,18 @@ func (g *Game) Destroy() {
|
||||
g.ctx.Destroy()
|
||||
}
|
||||
|
||||
func (g *Game) Init(disp *allg5.Display, res vfs.CopyDir, fps *FPS) error {
|
||||
g.ctx = &Context{Resources: res, Bitmaps: map[string]*allg5.Bitmap{}}
|
||||
if err := g.initUI(disp, fps); err != nil {
|
||||
func (g *Game) Init(disp *allg5.Display, res vfs.CopyDir, cons *gut.Console, fps *gut.FPS) error {
|
||||
log.Print("Initializing game...")
|
||||
g.ctx = &Context{Resources: res, Textures: map[string]Texture{}}
|
||||
if err := g.initUI(disp, cons, fps); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Print("Loading assets...")
|
||||
if err := g.loadAssets(); err != nil {
|
||||
return err
|
||||
}
|
||||
scene := g.PlayScene()
|
||||
scene.init(g.ctx)
|
||||
scene.loadLevel("1")
|
||||
log.Print("Loaded assets.")
|
||||
g.playLevel("1")
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -133,10 +192,25 @@ func (g *Game) Handle(e allg5.Event) {
|
||||
if e.KeyCode == allg5.KeyF3 {
|
||||
g.info.Visible = !g.info.Visible
|
||||
}
|
||||
if e.KeyCode == allg5.KeyF4 {
|
||||
g.cons.Visible = !g.cons.Visible
|
||||
}
|
||||
}
|
||||
g.ui.Handle(e)
|
||||
}
|
||||
|
||||
func (g *Game) playLevel(l string) {
|
||||
play := &playLevel{ctx: g.ctx}
|
||||
play.loadLevel(l)
|
||||
g.scene.Proxy = play
|
||||
}
|
||||
|
||||
func (g *Game) Render() {
|
||||
switch scene := g.scene.Proxy.(type) {
|
||||
case *splash:
|
||||
if scene.atEnd {
|
||||
g.playLevel("1")
|
||||
}
|
||||
}
|
||||
g.ui.Render()
|
||||
}
|
||||
|
@ -6,12 +6,13 @@ import (
|
||||
"opslag.de/schobers/allg5"
|
||||
"opslag.de/schobers/geom"
|
||||
"opslag.de/schobers/krampus19/alui"
|
||||
"opslag.de/schobers/krampus19/gut"
|
||||
)
|
||||
|
||||
type info struct {
|
||||
alui.ControlBase
|
||||
|
||||
fps *FPS
|
||||
fps *gut.FPS
|
||||
}
|
||||
|
||||
func (i *info) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
|
@ -1,13 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
rice "github.com/GeertJohan/go.rice"
|
||||
"github.com/spf13/afero"
|
||||
"opslag.de/schobers/allg5"
|
||||
"opslag.de/schobers/fs/ricefs"
|
||||
"opslag.de/schobers/fs/vfs"
|
||||
"opslag.de/schobers/krampus19/gut"
|
||||
)
|
||||
|
||||
//go:generate rice embed-syso
|
||||
@ -26,11 +29,16 @@ func resources() (vfs.CopyDir, error) {
|
||||
}
|
||||
|
||||
func run() error {
|
||||
cons := &gut.Console{}
|
||||
log.SetOutput(io.MultiWriter(log.Writer(), cons))
|
||||
|
||||
log.Println("Initializing Allegro")
|
||||
err := allg5.Init(allg5.InitAll)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("Creating display")
|
||||
disp, err := allg5.NewDisplay(1440, 900, allg5.NewDisplayOptions{Maximized: false, Windowed: true, Resizable: true, Vsync: true})
|
||||
if err != nil {
|
||||
return err
|
||||
@ -53,18 +61,22 @@ func run() error {
|
||||
}
|
||||
defer res.Destroy()
|
||||
|
||||
fps := NewFPS()
|
||||
fps := gut.NewFPS()
|
||||
defer fps.Destroy()
|
||||
|
||||
game := &Game{}
|
||||
err = game.Init(disp, res, fps)
|
||||
err = game.Init(disp, res, cons, fps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer game.Destroy()
|
||||
|
||||
log.Println("Starting game loop")
|
||||
back := allg5.NewColor(0x21, 0x21, 0x21)
|
||||
start := time.Now()
|
||||
for {
|
||||
game.ctx.Tick = time.Now().Sub(start)
|
||||
|
||||
allg5.ClearToColor(back)
|
||||
game.Render()
|
||||
disp.Flip()
|
||||
|
@ -58,25 +58,16 @@ type level struct {
|
||||
|
||||
func loadLevelAsset(r io.Reader) (level, error) {
|
||||
var l level
|
||||
ctx := levelContext{&l, nil}
|
||||
ctx := levelContext{&l}
|
||||
err := parseLines(r, ctx.parse)
|
||||
if err != nil {
|
||||
return level{}, err
|
||||
}
|
||||
if ctx.err != nil {
|
||||
return level{}, ctx.err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
type levelContext struct {
|
||||
level *level
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *levelContext) emitErr(err error) parseLineFn {
|
||||
c.err = err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *levelContext) parse(p *lineParser) parseLineFn {
|
||||
@ -96,14 +87,14 @@ func (c *levelContext) parse(p *lineParser) parseLineFn {
|
||||
|
||||
func (c *levelContext) parseContent(p *lineParser) parseLineFn {
|
||||
if p.next() != "level:" {
|
||||
return c.emitErr(errors.New("expected level start"))
|
||||
return p.emitErr(errors.New("expected level start"))
|
||||
}
|
||||
return c.parseRow
|
||||
}
|
||||
|
||||
func (c *levelContext) parseRow(p *lineParser) parseLineFn {
|
||||
if p.eof() {
|
||||
return c.emitErr(errors.New("unexpected end of file"))
|
||||
return p.emitErr(errors.New("unexpected end of file"))
|
||||
}
|
||||
line := p.next()
|
||||
if line == ":level" {
|
||||
@ -112,10 +103,10 @@ func (c *levelContext) parseRow(p *lineParser) parseLineFn {
|
||||
if c.level.height == 0 {
|
||||
c.level.width = len(line) / 2
|
||||
}
|
||||
return c.addRow(line)
|
||||
return c.addRow(p, line)
|
||||
}
|
||||
|
||||
func (c *levelContext) addRow(line string) parseLineFn {
|
||||
func (c *levelContext) addRow(p *lineParser, line string) parseLineFn {
|
||||
var tiles []tile
|
||||
var entities []entity
|
||||
for i := 0; i < len(line); i += 2 {
|
||||
@ -125,12 +116,12 @@ func (c *levelContext) addRow(line string) parseLineFn {
|
||||
|
||||
for i, t := range tiles {
|
||||
if !t.IsValid() {
|
||||
return c.emitErr(fmt.Errorf("level contains invalid tile at (%d, %d)", i, c.level.height))
|
||||
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 c.emitErr(fmt.Errorf("level contains invalid entity at (%d, %d)", i, c.level.height))
|
||||
return p.emitErr(fmt.Errorf("level contains invalid entity at (%d, %d)", i, c.level.height))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
@ -9,8 +10,11 @@ import (
|
||||
type lineParser struct {
|
||||
lines []string
|
||||
i int
|
||||
err error
|
||||
}
|
||||
|
||||
var errUnexpectedEnd = errors.New("unexpected end of file")
|
||||
|
||||
func (p *lineParser) eof() bool { return p.i == len(p.lines) }
|
||||
func (p *lineParser) peek() string { return p.lines[p.i] }
|
||||
func (p *lineParser) next() string {
|
||||
@ -19,6 +23,18 @@ func (p *lineParser) next() string {
|
||||
return p.lines[i]
|
||||
}
|
||||
|
||||
func (p *lineParser) emitErr(err error) parseLineFn {
|
||||
p.err = err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *lineParser) skipSpaceEOF() bool {
|
||||
for !p.eof() && len(strings.TrimSpace(p.peek())) == 0 {
|
||||
p.next()
|
||||
}
|
||||
return p.eof()
|
||||
}
|
||||
|
||||
type parseLineFn func(p *lineParser) parseLineFn
|
||||
|
||||
func parseLines(r io.Reader, fn parseLineFn) error {
|
||||
@ -35,5 +51,5 @@ func parseLines(r io.Reader, fn parseLineFn) error {
|
||||
for fn != nil {
|
||||
fn = fn(parser)
|
||||
}
|
||||
return nil
|
||||
return parser.err
|
||||
}
|
||||
|
@ -6,25 +6,23 @@ import (
|
||||
"opslag.de/schobers/krampus19/alui"
|
||||
)
|
||||
|
||||
type playScene struct {
|
||||
type playLevel struct {
|
||||
alui.ControlBase
|
||||
|
||||
ctx *Context
|
||||
level level
|
||||
|
||||
ctx *Context
|
||||
offset geom.PointF32
|
||||
scale float32
|
||||
|
||||
level level
|
||||
player geom.PointF32
|
||||
moving bool
|
||||
}
|
||||
|
||||
func (s *playScene) init(ctx *Context) {
|
||||
s.ctx = ctx
|
||||
}
|
||||
|
||||
func (s *playScene) loadLevel(name string) {
|
||||
func (s *playLevel) loadLevel(name string) {
|
||||
s.level = s.ctx.Levels[name]
|
||||
}
|
||||
|
||||
func (s *playScene) toScreenPos(p geom.Point) geom.PointF32 {
|
||||
func (s *playLevel) toScreenPos(p geom.Point) geom.PointF32 {
|
||||
pos := geom.PtF32(float32(p.X)+.5, float32(p.Y)+.5)
|
||||
pos = geom.PtF32(pos.X*148-pos.Y*46, pos.Y*82)
|
||||
pos = geom.PtF32(pos.X*s.scale, pos.Y*s.scale)
|
||||
@ -44,7 +42,7 @@ func (s *playScene) toScreenPos(p geom.Point) geom.PointF32 {
|
||||
// Offset between horizontal tiles: 148,0
|
||||
// Offset between vertical tiles: -46,82
|
||||
|
||||
func (s *playScene) Layout(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
func (s *playLevel) Layout(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
s.scale = bounds.Dy() / 1080
|
||||
|
||||
tilesCenter := geom.PtF32(.5*float32(s.level.width), .5*float32(s.level.height))
|
||||
@ -54,25 +52,25 @@ func (s *playScene) Layout(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
s.offset = geom.PtF32(center.X-tilesCenter.X, center.Y-tilesCenter.Y)
|
||||
}
|
||||
|
||||
func (s *playScene) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
basicTile := s.ctx.Bitmaps["basic_tile"]
|
||||
waterTile := s.ctx.Bitmaps["water_tile"]
|
||||
tileBmp := func(t tile) *allg5.Bitmap {
|
||||
func (s *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
basicTile := s.ctx.Textures["basic_tile"]
|
||||
waterTile := s.ctx.Textures["water_tile"]
|
||||
tileBmp := func(t tile) Texture {
|
||||
switch t {
|
||||
case tileBasic:
|
||||
return basicTile
|
||||
case tileWater:
|
||||
return waterTile
|
||||
default:
|
||||
return nil
|
||||
return Texture{}
|
||||
}
|
||||
}
|
||||
|
||||
character := s.ctx.Bitmaps["main_character"]
|
||||
villain := s.ctx.Bitmaps["villain_character"]
|
||||
brick := s.ctx.Bitmaps["brick"]
|
||||
crate := s.ctx.Bitmaps["crate"]
|
||||
entityBmp := func(e entity) *allg5.Bitmap {
|
||||
character := s.ctx.Textures["main_character"]
|
||||
villain := s.ctx.Textures["villain_character"]
|
||||
brick := s.ctx.Textures["brick"]
|
||||
crate := s.ctx.Textures["crate"]
|
||||
entityBmp := func(e entity) Texture {
|
||||
switch e {
|
||||
case entityCharacter:
|
||||
return character
|
||||
@ -83,7 +81,7 @@ func (s *playScene) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
case entityCrate:
|
||||
return crate
|
||||
default:
|
||||
return nil
|
||||
return Texture{}
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,7 +92,7 @@ func (s *playScene) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
level := s.level
|
||||
for i, t := range level.tiles {
|
||||
tile := tileBmp(t)
|
||||
if tile == nil {
|
||||
if tile.Bitmap == nil {
|
||||
continue
|
||||
}
|
||||
pos := geom.Pt(i%level.width, i/level.width)
|
||||
@ -107,16 +105,16 @@ func (s *playScene) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
|
||||
for i, e := range level.entities {
|
||||
bmp := entityBmp(e)
|
||||
if bmp == nil {
|
||||
if bmp.Bitmap == nil {
|
||||
continue
|
||||
}
|
||||
pos := geom.Pt(i%level.width, i/level.width)
|
||||
screenPos := s.toScreenPos(pos)
|
||||
screenPos.Y -= 48 * s.scale
|
||||
scale := scale
|
||||
if e == entityCharacter {
|
||||
scale *= .4
|
||||
}
|
||||
// scale := scale
|
||||
// if e == entityCharacter {
|
||||
// scale *= .4
|
||||
// }
|
||||
bmp.DrawOptions(screenPos.X, screenPos.Y, allg5.DrawOptions{Center: true, Scale: allg5.NewUniformScale(s.scale * scale)})
|
||||
}
|
||||
}
|
Binary file not shown.
93
cmd/krampus19/res/fonts/FiraMono-OFL.txt
Normal file
93
cmd/krampus19/res/fonts/FiraMono-OFL.txt
Normal file
@ -0,0 +1,93 @@
|
||||
Copyright (c) 2012-2013, The Mozilla Corporation and Telefonica S.A.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
cmd/krampus19/res/fonts/FiraMono-Regular.ttf
Normal file
BIN
cmd/krampus19/res/fonts/FiraMono-Regular.ttf
Normal file
Binary file not shown.
202
cmd/krampus19/res/fonts/OpenSans-LICENSE.txt
Normal file
202
cmd/krampus19/res/fonts/OpenSans-LICENSE.txt
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
BIN
cmd/krampus19/res/fonts/OpenSans-Regular.ttf
Normal file
BIN
cmd/krampus19/res/fonts/OpenSans-Regular.ttf
Normal file
Binary file not shown.
@ -4,8 +4,8 @@ level:
|
||||
._._#_._#_._._._._._
|
||||
._._#_#_#_._._._._._
|
||||
._._#_._#B._._._._._
|
||||
._#@#_#_#_~_~_#_#X._
|
||||
._._._._~_~_~_#_._._
|
||||
._._._#_#_#_#_#_._._
|
||||
._#@#_#_#_~_#_#_#X._
|
||||
._._._._~_~_#_._._._
|
||||
._._._#_#_#_#_._._._
|
||||
._._._._._._._._._._
|
||||
:level
|
28
cmd/krampus19/res/sprites/dragon.txt
Normal file
28
cmd/krampus19/res/sprites/dragon.txt
Normal file
@ -0,0 +1,28 @@
|
||||
sprite:
|
||||
texture: dragon
|
||||
|
||||
part:
|
||||
name: body
|
||||
sub_texture: 0,0,2107,1284
|
||||
anchor: 1065,594
|
||||
:part
|
||||
|
||||
part:
|
||||
name: foot
|
||||
sub_texture: 42,1345,529,856
|
||||
anchor: 386,1283
|
||||
:part
|
||||
|
||||
part:
|
||||
name: upper_wing
|
||||
sub_texture: 2112,39,1080,651
|
||||
anchor: 2413,644
|
||||
:part
|
||||
|
||||
part:
|
||||
name: lower_wing
|
||||
sub_texture: 2170,764,1030,736
|
||||
anchor: 2658,799
|
||||
:part
|
||||
|
||||
:sprite
|
@ -1,7 +1,45 @@
|
||||
package main
|
||||
|
||||
import "opslag.de/schobers/krampus19/alui"
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"opslag.de/schobers/allg5"
|
||||
|
||||
"opslag.de/schobers/geom"
|
||||
"opslag.de/schobers/krampus19/alui"
|
||||
)
|
||||
|
||||
type splash struct {
|
||||
alui.Container
|
||||
|
||||
ctx *Context
|
||||
atEnd bool
|
||||
|
||||
tick time.Duration
|
||||
}
|
||||
|
||||
func (s *splash) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||
var dragon = s.ctx.Sprites["dragon"]
|
||||
|
||||
var texture = s.ctx.Textures[dragon.texture]
|
||||
var body = texture.Subs["body"]
|
||||
var upperWing = texture.Subs["upper_wing"]
|
||||
var lowerWing = texture.Subs["lower_wing"]
|
||||
var foot = texture.Subs["foot"]
|
||||
|
||||
scale := bounds.Dy() / 1600
|
||||
|
||||
var repeat = 2 * time.Second / time.Millisecond
|
||||
var tick = float64((s.ctx.Tick / time.Millisecond) % repeat)
|
||||
flap := .5*float32(math.Cos(tick*2*math.Pi/float64(repeat))) + .5 // [0..1]
|
||||
upperFlap := flap*.75 + .25 // [.25..1]
|
||||
lowerFlap := flap*.5 + .5 // [.5..1]
|
||||
|
||||
body.DrawOptions(0, 0, allg5.DrawOptions{Scale: allg5.NewUniformScale(scale)})
|
||||
foot.DrawOptions(750*scale, 500*scale, allg5.DrawOptions{Scale: allg5.NewUniformScale(scale)})
|
||||
|
||||
var upperWingOffset = float32(upperWing.Height()) * scale * (1 - flap) * .8
|
||||
upperWing.DrawOptions(750*scale, -100*scale+upperWingOffset, allg5.DrawOptions{Scale: allg5.NewScale(scale, scale*upperFlap)})
|
||||
lowerWing.DrawOptions(750*scale, -100*scale+upperWingOffset*.95, allg5.DrawOptions{Scale: allg5.NewScale(scale, scale*lowerFlap)})
|
||||
}
|
||||
|
126
cmd/krampus19/sprite.go
Normal file
126
cmd/krampus19/sprite.go
Normal file
@ -0,0 +1,126 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"opslag.de/schobers/geom"
|
||||
)
|
||||
|
||||
type sprite struct {
|
||||
texture string
|
||||
parts []spritePart
|
||||
}
|
||||
|
||||
type spritePart struct {
|
||||
name string
|
||||
sub geom.Rectangle
|
||||
anchor geom.Point
|
||||
}
|
||||
|
||||
func loadSpriteAsset(r io.Reader) (sprite, error) {
|
||||
var l sprite
|
||||
ctx := spriteContext{&l, nil}
|
||||
err := parseLines(r, ctx.parse)
|
||||
if err != nil {
|
||||
return sprite{}, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
type spriteContext struct {
|
||||
sprite *sprite
|
||||
part *spritePart
|
||||
}
|
||||
|
||||
func (c *spriteContext) parse(p *lineParser) parseLineFn {
|
||||
if p.skipSpaceEOF() {
|
||||
return nil
|
||||
}
|
||||
line := p.peek()
|
||||
switch {
|
||||
case strings.HasPrefix(line, "sprite:"):
|
||||
p.next()
|
||||
return c.parseContent
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *spriteContext) parseContent(p *lineParser) parseLineFn {
|
||||
const partTag = "part:"
|
||||
const textureTag = "texture:"
|
||||
const spriteEndTag = ":sprite"
|
||||
if p.skipSpaceEOF() {
|
||||
return p.emitErr(errUnexpectedEnd)
|
||||
}
|
||||
line := p.peek()
|
||||
switch {
|
||||
case strings.HasPrefix(line, textureTag):
|
||||
c.sprite.texture = strings.TrimSpace(line[len(textureTag):])
|
||||
p.next()
|
||||
return c.parseContent
|
||||
case line == partTag:
|
||||
p.next()
|
||||
c.part = &spritePart{}
|
||||
return c.parsePart
|
||||
case line == spriteEndTag:
|
||||
return nil
|
||||
}
|
||||
return p.emitErr(errors.New("unexpected content of sprite"))
|
||||
}
|
||||
|
||||
func (c *spriteContext) parsePart(p *lineParser) parseLineFn {
|
||||
mustAtois := func(s ...string) []int {
|
||||
res := make([]int, len(s))
|
||||
var err error
|
||||
for i, s := range s {
|
||||
res[i], err = strconv.Atoi(s)
|
||||
if err != nil {
|
||||
panic("string does not represent an integer")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
mustCoords := func(s string) []int {
|
||||
coords := strings.Split(strings.TrimSpace(s), ",")
|
||||
return mustAtois(coords...)
|
||||
}
|
||||
const nameTag = "name:"
|
||||
const subTextureTag = "sub_texture:"
|
||||
const anchorTag = "anchor:"
|
||||
const partEndTag = ":part"
|
||||
if p.skipSpaceEOF() {
|
||||
return p.emitErr(errUnexpectedEnd)
|
||||
}
|
||||
line := p.peek()
|
||||
switch {
|
||||
case strings.HasPrefix(line, nameTag):
|
||||
c.part.name = strings.TrimSpace(line[len(nameTag):])
|
||||
p.next()
|
||||
return c.parsePart
|
||||
case strings.HasPrefix(line, subTextureTag):
|
||||
var coords = mustCoords(line[len(subTextureTag):])
|
||||
if len(coords) != 4 {
|
||||
panic("expected four coordinates (min x, min y, size x, size y)")
|
||||
}
|
||||
c.part.sub = geom.Rect(coords[0], coords[1], coords[0]+coords[2], coords[1]+coords[3])
|
||||
p.next()
|
||||
return c.parsePart
|
||||
case strings.HasPrefix(line, anchorTag):
|
||||
var coords = mustCoords(line[len(anchorTag):])
|
||||
if len(coords) != 2 {
|
||||
panic("expected two coordinates (min x, min y)")
|
||||
}
|
||||
c.part.anchor = geom.Pt(coords[0], coords[1])
|
||||
p.next()
|
||||
return c.parsePart
|
||||
case line == partEndTag:
|
||||
c.sprite.parts = append(c.sprite.parts, *c.part)
|
||||
p.next()
|
||||
return c.parseContent
|
||||
}
|
||||
return p.emitErr(errors.New("unexpected content of part"))
|
||||
}
|
21
gut/console.go
Normal file
21
gut/console.go
Normal file
@ -0,0 +1,21 @@
|
||||
package gut
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
var _ io.Writer = &Console{}
|
||||
|
||||
type Console struct {
|
||||
messages []string
|
||||
}
|
||||
|
||||
func (c *Console) Write(p []byte) (int, error) {
|
||||
c.messages = append(c.messages, string(bytes.TrimRight(p, "\r\n")))
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (c *Console) Messages() []string {
|
||||
return c.messages
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package main
|
||||
package gut
|
||||
|
||||
import "time"
|
||||
|
Loading…
Reference in New Issue
Block a user