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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
"opslag.de/schobers/fs/vfs"
|
"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 {
|
type Context struct {
|
||||||
Resources vfs.CopyDir
|
Resources vfs.CopyDir
|
||||||
Bitmaps map[string]*allg5.Bitmap
|
Textures map[string]Texture
|
||||||
Levels map[string]level
|
Levels map[string]level
|
||||||
|
Sprites map[string]sprite
|
||||||
|
|
||||||
|
Tick time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Destroy() {
|
func (c *Context) Destroy() {
|
||||||
for _, bmp := range c.Bitmaps {
|
for _, bmp := range c.Textures {
|
||||||
bmp.Destroy()
|
bmp.Destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image/png"
|
"image/png"
|
||||||
|
"log"
|
||||||
|
|
||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
"opslag.de/schobers/fs/vfs"
|
"opslag.de/schobers/fs/vfs"
|
||||||
"opslag.de/schobers/krampus19/alui"
|
"opslag.de/schobers/krampus19/alui"
|
||||||
|
"opslag.de/schobers/krampus19/gut"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
@ -15,24 +18,18 @@ type Game struct {
|
|||||||
main alui.Container
|
main alui.Container
|
||||||
|
|
||||||
info *alui.Overlay
|
info *alui.Overlay
|
||||||
|
cons *alui.Overlay
|
||||||
scene *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")
|
disp.SetWindowTitle("Krampushack '19 - Title TBD - by Tharro")
|
||||||
ui := alui.NewUI(disp, &g.main)
|
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.ui = ui
|
||||||
g.info = &alui.Overlay{Proxy: &info{fps: fps}}
|
g.info = &alui.Overlay{Proxy: &info{fps: fps}}
|
||||||
g.scene = &alui.Overlay{Proxy: &playScene{}, Visible: true}
|
g.cons = &alui.Overlay{Proxy: newConsole(cons)}
|
||||||
g.main.Children = append(g.main.Children, g.scene, g.info)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,11 +47,31 @@ func (g *Game) loadBitmap(path, name string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
g.ctx.Bitmaps[name] = bmp
|
g.ctx.Textures[name] = NewTexture(bmp)
|
||||||
return nil
|
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 {
|
for path, name := range pathToName {
|
||||||
err := g.loadBitmap(path, name)
|
err := g.loadBitmap(path, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -82,11 +99,40 @@ func (g *Game) loadLevels(names ...string) error {
|
|||||||
return nil
|
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 {
|
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",
|
"basic_tile.png": "basic_tile",
|
||||||
"water_tile.png": "water_tile",
|
"water_tile.png": "water_tile",
|
||||||
|
|
||||||
|
// "dragon.png": "dragon",
|
||||||
"main_character.png": "main_character",
|
"main_character.png": "main_character",
|
||||||
"villain_character.png": "villain_character",
|
"villain_character.png": "villain_character",
|
||||||
|
|
||||||
@ -96,16 +142,28 @@ func (g *Game) loadAssets() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
log.Printf("Loaded %d textures.\n", len(g.ctx.Textures))
|
||||||
|
|
||||||
|
log.Println("Loading levels...")
|
||||||
err = g.loadLevels("1")
|
err = g.loadLevels("1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
log.Printf("Loaded %d levels.\n", len(g.ctx.Levels))
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Game) PlayScene() *playScene {
|
log.Println("Loading fonts...")
|
||||||
return g.scene.Proxy.(*playScene)
|
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() {
|
func (g *Game) Destroy() {
|
||||||
@ -113,17 +171,18 @@ func (g *Game) Destroy() {
|
|||||||
g.ctx.Destroy()
|
g.ctx.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Init(disp *allg5.Display, res vfs.CopyDir, fps *FPS) error {
|
func (g *Game) Init(disp *allg5.Display, res vfs.CopyDir, cons *gut.Console, fps *gut.FPS) error {
|
||||||
g.ctx = &Context{Resources: res, Bitmaps: map[string]*allg5.Bitmap{}}
|
log.Print("Initializing game...")
|
||||||
if err := g.initUI(disp, fps); err != nil {
|
g.ctx = &Context{Resources: res, Textures: map[string]Texture{}}
|
||||||
|
if err := g.initUI(disp, cons, fps); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
log.Print("Loading assets...")
|
||||||
if err := g.loadAssets(); err != nil {
|
if err := g.loadAssets(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
scene := g.PlayScene()
|
log.Print("Loaded assets.")
|
||||||
scene.init(g.ctx)
|
g.playLevel("1")
|
||||||
scene.loadLevel("1")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,10 +192,25 @@ func (g *Game) Handle(e allg5.Event) {
|
|||||||
if e.KeyCode == allg5.KeyF3 {
|
if e.KeyCode == allg5.KeyF3 {
|
||||||
g.info.Visible = !g.info.Visible
|
g.info.Visible = !g.info.Visible
|
||||||
}
|
}
|
||||||
|
if e.KeyCode == allg5.KeyF4 {
|
||||||
|
g.cons.Visible = !g.cons.Visible
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g.ui.Handle(e)
|
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() {
|
func (g *Game) Render() {
|
||||||
|
switch scene := g.scene.Proxy.(type) {
|
||||||
|
case *splash:
|
||||||
|
if scene.atEnd {
|
||||||
|
g.playLevel("1")
|
||||||
|
}
|
||||||
|
}
|
||||||
g.ui.Render()
|
g.ui.Render()
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,13 @@ import (
|
|||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
"opslag.de/schobers/krampus19/alui"
|
"opslag.de/schobers/krampus19/alui"
|
||||||
|
"opslag.de/schobers/krampus19/gut"
|
||||||
)
|
)
|
||||||
|
|
||||||
type info struct {
|
type info struct {
|
||||||
alui.ControlBase
|
alui.ControlBase
|
||||||
|
|
||||||
fps *FPS
|
fps *gut.FPS
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *info) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
func (i *info) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
rice "github.com/GeertJohan/go.rice"
|
rice "github.com/GeertJohan/go.rice"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
"opslag.de/schobers/fs/ricefs"
|
"opslag.de/schobers/fs/ricefs"
|
||||||
"opslag.de/schobers/fs/vfs"
|
"opslag.de/schobers/fs/vfs"
|
||||||
|
"opslag.de/schobers/krampus19/gut"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate rice embed-syso
|
//go:generate rice embed-syso
|
||||||
@ -26,11 +29,16 @@ func resources() (vfs.CopyDir, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func run() error {
|
func run() error {
|
||||||
|
cons := &gut.Console{}
|
||||||
|
log.SetOutput(io.MultiWriter(log.Writer(), cons))
|
||||||
|
|
||||||
|
log.Println("Initializing Allegro")
|
||||||
err := allg5.Init(allg5.InitAll)
|
err := allg5.Init(allg5.InitAll)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Println("Creating display")
|
||||||
disp, err := allg5.NewDisplay(1440, 900, allg5.NewDisplayOptions{Maximized: false, Windowed: true, Resizable: true, Vsync: true})
|
disp, err := allg5.NewDisplay(1440, 900, allg5.NewDisplayOptions{Maximized: false, Windowed: true, Resizable: true, Vsync: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -53,18 +61,22 @@ func run() error {
|
|||||||
}
|
}
|
||||||
defer res.Destroy()
|
defer res.Destroy()
|
||||||
|
|
||||||
fps := NewFPS()
|
fps := gut.NewFPS()
|
||||||
defer fps.Destroy()
|
defer fps.Destroy()
|
||||||
|
|
||||||
game := &Game{}
|
game := &Game{}
|
||||||
err = game.Init(disp, res, fps)
|
err = game.Init(disp, res, cons, fps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer game.Destroy()
|
defer game.Destroy()
|
||||||
|
|
||||||
|
log.Println("Starting game loop")
|
||||||
back := allg5.NewColor(0x21, 0x21, 0x21)
|
back := allg5.NewColor(0x21, 0x21, 0x21)
|
||||||
|
start := time.Now()
|
||||||
for {
|
for {
|
||||||
|
game.ctx.Tick = time.Now().Sub(start)
|
||||||
|
|
||||||
allg5.ClearToColor(back)
|
allg5.ClearToColor(back)
|
||||||
game.Render()
|
game.Render()
|
||||||
disp.Flip()
|
disp.Flip()
|
||||||
|
@ -58,25 +58,16 @@ type level struct {
|
|||||||
|
|
||||||
func loadLevelAsset(r io.Reader) (level, error) {
|
func loadLevelAsset(r io.Reader) (level, error) {
|
||||||
var l level
|
var l level
|
||||||
ctx := levelContext{&l, nil}
|
ctx := levelContext{&l}
|
||||||
err := parseLines(r, ctx.parse)
|
err := parseLines(r, ctx.parse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return level{}, err
|
return level{}, err
|
||||||
}
|
}
|
||||||
if ctx.err != nil {
|
|
||||||
return level{}, ctx.err
|
|
||||||
}
|
|
||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type levelContext struct {
|
type levelContext struct {
|
||||||
level *level
|
level *level
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *levelContext) emitErr(err error) parseLineFn {
|
|
||||||
c.err = err
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *levelContext) parse(p *lineParser) parseLineFn {
|
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 {
|
func (c *levelContext) parseContent(p *lineParser) parseLineFn {
|
||||||
if p.next() != "level:" {
|
if p.next() != "level:" {
|
||||||
return c.emitErr(errors.New("expected level start"))
|
return p.emitErr(errors.New("expected level start"))
|
||||||
}
|
}
|
||||||
return c.parseRow
|
return c.parseRow
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *levelContext) parseRow(p *lineParser) parseLineFn {
|
func (c *levelContext) parseRow(p *lineParser) parseLineFn {
|
||||||
if p.eof() {
|
if p.eof() {
|
||||||
return c.emitErr(errors.New("unexpected end of file"))
|
return p.emitErr(errors.New("unexpected end of file"))
|
||||||
}
|
}
|
||||||
line := p.next()
|
line := p.next()
|
||||||
if line == ":level" {
|
if line == ":level" {
|
||||||
@ -112,10 +103,10 @@ func (c *levelContext) parseRow(p *lineParser) parseLineFn {
|
|||||||
if c.level.height == 0 {
|
if c.level.height == 0 {
|
||||||
c.level.width = len(line) / 2
|
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 tiles []tile
|
||||||
var entities []entity
|
var entities []entity
|
||||||
for i := 0; i < len(line); i += 2 {
|
for i := 0; i < len(line); i += 2 {
|
||||||
@ -125,12 +116,12 @@ func (c *levelContext) addRow(line string) parseLineFn {
|
|||||||
|
|
||||||
for i, t := range tiles {
|
for i, t := range tiles {
|
||||||
if !t.IsValid() {
|
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 {
|
for i, e := range entities {
|
||||||
if !e.IsValid() {
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
@ -9,8 +10,11 @@ import (
|
|||||||
type lineParser struct {
|
type lineParser struct {
|
||||||
lines []string
|
lines []string
|
||||||
i int
|
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) eof() bool { return p.i == len(p.lines) }
|
||||||
func (p *lineParser) peek() string { return p.lines[p.i] }
|
func (p *lineParser) peek() string { return p.lines[p.i] }
|
||||||
func (p *lineParser) next() string {
|
func (p *lineParser) next() string {
|
||||||
@ -19,6 +23,18 @@ func (p *lineParser) next() string {
|
|||||||
return p.lines[i]
|
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
|
type parseLineFn func(p *lineParser) parseLineFn
|
||||||
|
|
||||||
func parseLines(r io.Reader, fn parseLineFn) error {
|
func parseLines(r io.Reader, fn parseLineFn) error {
|
||||||
@ -35,5 +51,5 @@ func parseLines(r io.Reader, fn parseLineFn) error {
|
|||||||
for fn != nil {
|
for fn != nil {
|
||||||
fn = fn(parser)
|
fn = fn(parser)
|
||||||
}
|
}
|
||||||
return nil
|
return parser.err
|
||||||
}
|
}
|
||||||
|
@ -6,25 +6,23 @@ import (
|
|||||||
"opslag.de/schobers/krampus19/alui"
|
"opslag.de/schobers/krampus19/alui"
|
||||||
)
|
)
|
||||||
|
|
||||||
type playScene struct {
|
type playLevel struct {
|
||||||
alui.ControlBase
|
alui.ControlBase
|
||||||
|
|
||||||
ctx *Context
|
ctx *Context
|
||||||
level level
|
|
||||||
|
|
||||||
offset geom.PointF32
|
offset geom.PointF32
|
||||||
scale float32
|
scale float32
|
||||||
|
|
||||||
|
level level
|
||||||
|
player geom.PointF32
|
||||||
|
moving bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playScene) init(ctx *Context) {
|
func (s *playLevel) loadLevel(name string) {
|
||||||
s.ctx = ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *playScene) loadLevel(name string) {
|
|
||||||
s.level = s.ctx.Levels[name]
|
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(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*148-pos.Y*46, pos.Y*82)
|
||||||
pos = geom.PtF32(pos.X*s.scale, pos.Y*s.scale)
|
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 horizontal tiles: 148,0
|
||||||
// Offset between vertical tiles: -46,82
|
// 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
|
s.scale = bounds.Dy() / 1080
|
||||||
|
|
||||||
tilesCenter := geom.PtF32(.5*float32(s.level.width), .5*float32(s.level.height))
|
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)
|
s.offset = geom.PtF32(center.X-tilesCenter.X, center.Y-tilesCenter.Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playScene) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
func (s *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
basicTile := s.ctx.Bitmaps["basic_tile"]
|
basicTile := s.ctx.Textures["basic_tile"]
|
||||||
waterTile := s.ctx.Bitmaps["water_tile"]
|
waterTile := s.ctx.Textures["water_tile"]
|
||||||
tileBmp := func(t tile) *allg5.Bitmap {
|
tileBmp := func(t tile) Texture {
|
||||||
switch t {
|
switch t {
|
||||||
case tileBasic:
|
case tileBasic:
|
||||||
return basicTile
|
return basicTile
|
||||||
case tileWater:
|
case tileWater:
|
||||||
return waterTile
|
return waterTile
|
||||||
default:
|
default:
|
||||||
return nil
|
return Texture{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
character := s.ctx.Bitmaps["main_character"]
|
character := s.ctx.Textures["main_character"]
|
||||||
villain := s.ctx.Bitmaps["villain_character"]
|
villain := s.ctx.Textures["villain_character"]
|
||||||
brick := s.ctx.Bitmaps["brick"]
|
brick := s.ctx.Textures["brick"]
|
||||||
crate := s.ctx.Bitmaps["crate"]
|
crate := s.ctx.Textures["crate"]
|
||||||
entityBmp := func(e entity) *allg5.Bitmap {
|
entityBmp := func(e entity) Texture {
|
||||||
switch e {
|
switch e {
|
||||||
case entityCharacter:
|
case entityCharacter:
|
||||||
return character
|
return character
|
||||||
@ -83,7 +81,7 @@ func (s *playScene) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
|||||||
case entityCrate:
|
case entityCrate:
|
||||||
return crate
|
return crate
|
||||||
default:
|
default:
|
||||||
return nil
|
return Texture{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +92,7 @@ func (s *playScene) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
|||||||
level := s.level
|
level := s.level
|
||||||
for i, t := range level.tiles {
|
for i, t := range level.tiles {
|
||||||
tile := tileBmp(t)
|
tile := tileBmp(t)
|
||||||
if tile == nil {
|
if tile.Bitmap == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pos := geom.Pt(i%level.width, i/level.width)
|
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 {
|
for i, e := range level.entities {
|
||||||
bmp := entityBmp(e)
|
bmp := entityBmp(e)
|
||||||
if bmp == nil {
|
if bmp.Bitmap == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pos := geom.Pt(i%level.width, i/level.width)
|
pos := geom.Pt(i%level.width, i/level.width)
|
||||||
screenPos := s.toScreenPos(pos)
|
screenPos := s.toScreenPos(pos)
|
||||||
screenPos.Y -= 48 * s.scale
|
screenPos.Y -= 48 * s.scale
|
||||||
scale := scale
|
// scale := scale
|
||||||
if e == entityCharacter {
|
// if e == entityCharacter {
|
||||||
scale *= .4
|
// scale *= .4
|
||||||
}
|
// }
|
||||||
bmp.DrawOptions(screenPos.X, screenPos.Y, allg5.DrawOptions{Center: true, Scale: allg5.NewUniformScale(s.scale * scale)})
|
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._._._._._
|
._._#_._#B._._._._._
|
||||||
._#@#_#_#_~_~_#_#X._
|
._#@#_#_#_~_#_#_#X._
|
||||||
._._._._~_~_~_#_._._
|
._._._._~_~_#_._._._
|
||||||
._._._#_#_#_#_#_._._
|
._._._#_#_#_#_._._._
|
||||||
._._._._._._._._._._
|
._._._._._._._._._._
|
||||||
:level
|
: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
|
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 {
|
type splash struct {
|
||||||
alui.Container
|
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"
|
import "time"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user