Added simple tile rendering.
This commit is contained in:
commit
afaa190648
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# Visual Studio Code
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
# Go
|
||||||
|
__debug_bin
|
||||||
|
rice-box.go
|
||||||
|
*.rice-box.*
|
31
alui/button.go
Normal file
31
alui/button.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Control = &Button{}
|
||||||
|
|
||||||
|
type Button struct {
|
||||||
|
ControlBase
|
||||||
|
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Button) DesiredSize(ctx *Context) geom.PointF32 {
|
||||||
|
font := ctx.Fonts.Get(b.Font)
|
||||||
|
_, _, w, h := font.TextDimensions(b.Text)
|
||||||
|
return geom.PtF32(w+8, h+8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Button) Render(ctx *Context, bounds geom.RectangleF32) {
|
||||||
|
font := ctx.Fonts.Get(b.Font)
|
||||||
|
|
||||||
|
fore := ctx.Palette.Primary
|
||||||
|
if b.Over {
|
||||||
|
fore = ctx.Palette.Dark
|
||||||
|
ctx.Cursor = allg5.MouseCursorLink
|
||||||
|
}
|
||||||
|
font.Draw(bounds.Min.X+4, bounds.Min.Y+4, fore, allg5.AlignLeft, b.Text)
|
||||||
|
}
|
51
alui/container.go
Normal file
51
alui/container.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Container struct {
|
||||||
|
ControlBase
|
||||||
|
|
||||||
|
Children []Control
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Handle(e allg5.Event) {
|
||||||
|
c.ControlBase.Handle(e)
|
||||||
|
for _, child := range c.Children {
|
||||||
|
child.Handle(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) DesiredSize(ctx *Context) geom.PointF32 {
|
||||||
|
var size geom.PointF32
|
||||||
|
for _, child := range c.Children {
|
||||||
|
s := child.DesiredSize(ctx)
|
||||||
|
if geom.IsNaN32(s.X) || geom.IsNaN32(size.X) {
|
||||||
|
size.X = geom.NaN32()
|
||||||
|
} else {
|
||||||
|
size.X = geom.Max32(s.X, size.X)
|
||||||
|
}
|
||||||
|
if geom.IsNaN32(s.Y) || geom.IsNaN32(size.Y) {
|
||||||
|
size.Y = geom.NaN32()
|
||||||
|
} else {
|
||||||
|
size.Y = geom.Max32(s.Y, size.Y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Layout(ctx *Context, bounds geom.RectangleF32) {
|
||||||
|
c.ControlBase.Layout(ctx, bounds)
|
||||||
|
for _, child := range c.Children {
|
||||||
|
child.Layout(ctx, bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) Render(ctx *Context, bounds geom.RectangleF32) {
|
||||||
|
c.ControlBase.Render(ctx, bounds)
|
||||||
|
for _, child := range c.Children {
|
||||||
|
child.Render(ctx, bounds)
|
||||||
|
}
|
||||||
|
}
|
21
alui/context.go
Normal file
21
alui/context.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
Display *allg5.Display
|
||||||
|
Fonts *Fonts
|
||||||
|
Cursor allg5.MouseCursor
|
||||||
|
Palette Palette
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) DisplayBounds() geom.RectangleF32 {
|
||||||
|
return geom.RectF32(0, 0, float32(c.Display.Width()), float32(c.Display.Height()))
|
||||||
|
}
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
Font string
|
||||||
|
}
|
73
alui/control.go
Normal file
73
alui/control.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Control interface {
|
||||||
|
Bounds() geom.RectangleF32
|
||||||
|
|
||||||
|
Handle(allg5.Event)
|
||||||
|
|
||||||
|
DesiredSize(*Context) geom.PointF32
|
||||||
|
Layout(*Context, geom.RectangleF32)
|
||||||
|
Render(*Context, geom.RectangleF32)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bounds struct {
|
||||||
|
value geom.RectangleF32
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Control = &ControlBase{}
|
||||||
|
|
||||||
|
type ControlBase struct {
|
||||||
|
_Bounds geom.RectangleF32
|
||||||
|
Over bool
|
||||||
|
Font string
|
||||||
|
Foreground *allg5.Color
|
||||||
|
Background *allg5.Color
|
||||||
|
|
||||||
|
OnClick func()
|
||||||
|
OnEnter func()
|
||||||
|
OnLeave func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func MouseEventToPos(e allg5.MouseEvent) geom.PointF32 { return geom.PtF32(float32(e.X), float32(e.Y)) }
|
||||||
|
|
||||||
|
func (b *ControlBase) DesiredSize(*Context) geom.PointF32 { return geom.ZeroPtF32 }
|
||||||
|
|
||||||
|
func (b *ControlBase) Handle(e allg5.Event) {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *allg5.MouseMoveEvent:
|
||||||
|
pos := MouseEventToPos(e.MouseEvent)
|
||||||
|
over := pos.In(b._Bounds)
|
||||||
|
if over != b.Over {
|
||||||
|
b.Over = over
|
||||||
|
if over {
|
||||||
|
if b.OnEnter != nil {
|
||||||
|
b.OnEnter()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if b.OnLeave != nil {
|
||||||
|
b.OnLeave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *allg5.MouseButtonDownEvent:
|
||||||
|
if !b.Over {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if e.Button == allg5.MouseButtonLeft {
|
||||||
|
if b.OnClick != nil {
|
||||||
|
b.OnClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *ControlBase) Layout(_ *Context, bounds geom.RectangleF32) { b._Bounds = bounds }
|
||||||
|
|
||||||
|
func (b *ControlBase) Render(*Context, geom.RectangleF32) {}
|
||||||
|
|
||||||
|
func (b *ControlBase) Bounds() geom.RectangleF32 { return b._Bounds }
|
55
alui/fonts.go
Normal file
55
alui/fonts.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Fonts struct {
|
||||||
|
fonts map[string]*allg5.Font
|
||||||
|
}
|
||||||
|
|
||||||
|
type FontDescription struct {
|
||||||
|
Path string
|
||||||
|
Size int
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFonts() *Fonts {
|
||||||
|
return &Fonts{map[string]*allg5.Font{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) Destroy() {
|
||||||
|
for _, font := range f.fonts {
|
||||||
|
font.Destroy()
|
||||||
|
}
|
||||||
|
f.fonts = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) Load(path string, size int, name string) error {
|
||||||
|
font, err := allg5.LoadTTFFont(path, size)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if old := f.fonts[name]; old != nil {
|
||||||
|
old.Destroy()
|
||||||
|
}
|
||||||
|
f.fonts[name] = font
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) LoadFonts(descriptions ...FontDescription) error {
|
||||||
|
for _, desc := range descriptions {
|
||||||
|
err := f.Load(desc.Path, desc.Size, desc.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) Get(name string) *allg5.Font {
|
||||||
|
if name == "" {
|
||||||
|
return f.Get("default")
|
||||||
|
}
|
||||||
|
return f.fonts[name]
|
||||||
|
}
|
35
alui/label.go
Normal file
35
alui/label.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Control = &Label{}
|
||||||
|
|
||||||
|
type Label struct {
|
||||||
|
ControlBase
|
||||||
|
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Label) DesiredSize(ctx *Context) geom.PointF32 {
|
||||||
|
font := ctx.Fonts.Get(l.Font)
|
||||||
|
_, _, w, h := font.TextDimensions(l.Text)
|
||||||
|
return geom.PtF32(w+8, h+8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Label) Render(ctx *Context, bounds geom.RectangleF32) {
|
||||||
|
font := ctx.Fonts.Get(l.Font)
|
||||||
|
|
||||||
|
back := l.Background
|
||||||
|
fore := l.Foreground
|
||||||
|
if fore == nil {
|
||||||
|
fore = &ctx.Palette.Text
|
||||||
|
}
|
||||||
|
|
||||||
|
if back != nil {
|
||||||
|
allg5.DrawFilledRectangle(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, *back)
|
||||||
|
}
|
||||||
|
font.Draw(bounds.Min.X+4, bounds.Min.Y+4, *fore, allg5.AlignLeft, l.Text)
|
||||||
|
}
|
8
alui/orientation.go
Normal file
8
alui/orientation.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
type Orientation string
|
||||||
|
|
||||||
|
const (
|
||||||
|
OrientationVertical Orientation = "vertical"
|
||||||
|
OrientationHorizontal = "horizontal"
|
||||||
|
)
|
39
alui/overlay.go
Normal file
39
alui/overlay.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Overlay struct {
|
||||||
|
ControlBase
|
||||||
|
|
||||||
|
Proxy Control
|
||||||
|
|
||||||
|
Visible bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Overlay) DesiredSize(ctx *Context) geom.PointF32 {
|
||||||
|
if o.Visible {
|
||||||
|
return o.Proxy.DesiredSize(ctx)
|
||||||
|
}
|
||||||
|
return geom.ZeroPtF32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Overlay) Handle(e allg5.Event) {
|
||||||
|
if o.Visible {
|
||||||
|
o.Proxy.Handle(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Overlay) Render(ctx *Context, bounds geom.RectangleF32) {
|
||||||
|
if o.Visible {
|
||||||
|
o.Proxy.Render(ctx, bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Overlay) Layout(ctx *Context, bounds geom.RectangleF32) {
|
||||||
|
if o.Visible {
|
||||||
|
o.Proxy.Layout(ctx, bounds)
|
||||||
|
}
|
||||||
|
}
|
15
alui/palette.go
Normal file
15
alui/palette.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Palette struct {
|
||||||
|
Primary allg5.Color
|
||||||
|
Light allg5.Color
|
||||||
|
Dark allg5.Color
|
||||||
|
Text allg5.Color
|
||||||
|
TextLight allg5.Color
|
||||||
|
Icon allg5.Color
|
||||||
|
Accent allg5.Color
|
||||||
|
}
|
99
alui/stackpanel.go
Normal file
99
alui/stackpanel.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Control = &StackPanel{}
|
||||||
|
|
||||||
|
type StackPanel struct {
|
||||||
|
Container
|
||||||
|
|
||||||
|
Orientation Orientation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StackPanel) Handle(e allg5.Event) {
|
||||||
|
s.Container.Handle(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StackPanel) asLength(p geom.PointF32) float32 {
|
||||||
|
switch s.Orientation {
|
||||||
|
case OrientationHorizontal:
|
||||||
|
return p.X
|
||||||
|
default:
|
||||||
|
return p.Y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StackPanel) asWidth(p geom.PointF32) float32 {
|
||||||
|
switch s.Orientation {
|
||||||
|
case OrientationHorizontal:
|
||||||
|
return p.Y
|
||||||
|
default:
|
||||||
|
return p.X
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StackPanel) asSize(length, width float32) geom.PointF32 {
|
||||||
|
switch s.Orientation {
|
||||||
|
case OrientationHorizontal:
|
||||||
|
return geom.PtF32(length, width)
|
||||||
|
default:
|
||||||
|
return geom.PtF32(width, length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StackPanel) CalculateLayout(ctx *Context) ([]geom.PointF32, geom.PointF32) {
|
||||||
|
desired := make([]geom.PointF32, len(s.Children))
|
||||||
|
for i, child := range s.Children {
|
||||||
|
desired[i] = child.DesiredSize(ctx)
|
||||||
|
}
|
||||||
|
var length, width float32
|
||||||
|
for _, size := range desired {
|
||||||
|
w, l := s.asWidth(size), s.asLength(size)
|
||||||
|
if geom.IsNaN32(w) {
|
||||||
|
width = geom.NaN32()
|
||||||
|
} else if !geom.IsNaN32(width) {
|
||||||
|
width = geom.Max32(width, w)
|
||||||
|
}
|
||||||
|
if geom.IsNaN32(l) {
|
||||||
|
panic("not implemented")
|
||||||
|
}
|
||||||
|
length += l
|
||||||
|
}
|
||||||
|
switch s.Orientation {
|
||||||
|
case OrientationHorizontal:
|
||||||
|
return desired, geom.PtF32(length, width)
|
||||||
|
default:
|
||||||
|
return desired, geom.PtF32(width, length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StackPanel) DesiredSize(ctx *Context) geom.PointF32 {
|
||||||
|
_, size := s.CalculateLayout(ctx)
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StackPanel) Layout(ctx *Context, bounds geom.RectangleF32) {
|
||||||
|
s.Container.Layout(ctx, bounds)
|
||||||
|
|
||||||
|
desired, size := s.CalculateLayout(ctx)
|
||||||
|
width := s.asWidth(size)
|
||||||
|
|
||||||
|
var offset = bounds.Min
|
||||||
|
for i, child := range s.Children {
|
||||||
|
length := s.asLength(desired[i])
|
||||||
|
childSize := s.asSize(length, width)
|
||||||
|
var bottomRight = offset.Add(childSize)
|
||||||
|
var childBounds = geom.RectF32(offset.X, offset.Y, bottomRight.X, bottomRight.Y)
|
||||||
|
child.Layout(ctx, childBounds)
|
||||||
|
offset = offset.Add(s.asSize(length, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StackPanel) Render(ctx *Context, bounds geom.RectangleF32) {
|
||||||
|
for _, child := range s.Children {
|
||||||
|
child.Render(ctx, child.Bounds())
|
||||||
|
}
|
||||||
|
}
|
47
alui/ui.go
Normal file
47
alui/ui.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package alui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UI struct {
|
||||||
|
ctx *Context
|
||||||
|
main Control
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUI(disp *allg5.Display, main Control) *UI {
|
||||||
|
ctx := &Context{
|
||||||
|
Display: disp,
|
||||||
|
Fonts: NewFonts(),
|
||||||
|
Palette: Palette{},
|
||||||
|
}
|
||||||
|
return &UI{ctx, main}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UI) Context() *Context { return ui.ctx }
|
||||||
|
|
||||||
|
func (ui *UI) Destroy() {
|
||||||
|
ui.ctx.Fonts.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UI) Fonts() *Fonts { return ui.ctx.Fonts }
|
||||||
|
|
||||||
|
func (ui *UI) SetPalette(p Palette) { ui.ctx.Palette = p }
|
||||||
|
|
||||||
|
func (ui *UI) layoutBounds(bounds geom.RectangleF32) {
|
||||||
|
ui.main.Layout(ui.ctx, bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UI) Handle(e allg5.Event) { ui.main.Handle(e) }
|
||||||
|
|
||||||
|
func (ui *UI) Render() {
|
||||||
|
ui.RenderBounds(ui.ctx.DisplayBounds())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *UI) RenderBounds(bounds geom.RectangleF32) {
|
||||||
|
ui.ctx.Cursor = allg5.MouseCursorDefault
|
||||||
|
ui.layoutBounds(bounds)
|
||||||
|
ui.main.Render(ui.ctx, bounds)
|
||||||
|
ui.ctx.Display.SetMouseCursor(ui.ctx.Cursor)
|
||||||
|
}
|
18
cmd/krampus19/context.go
Normal file
18
cmd/krampus19/context.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/fs/vfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
Resources vfs.CopyDir
|
||||||
|
Bitmaps map[string]*allg5.Bitmap
|
||||||
|
Levels map[string]level
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Destroy() {
|
||||||
|
for _, bmp := range c.Bitmaps {
|
||||||
|
bmp.Destroy()
|
||||||
|
}
|
||||||
|
}
|
60
cmd/krampus19/fps.go
Normal file
60
cmd/krampus19/fps.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type FPS struct {
|
||||||
|
done chan struct{}
|
||||||
|
cnt chan struct{}
|
||||||
|
|
||||||
|
curr int
|
||||||
|
|
||||||
|
total int
|
||||||
|
frame int
|
||||||
|
frames []int
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFPS() *FPS {
|
||||||
|
fps := &FPS{
|
||||||
|
done: make(chan struct{}),
|
||||||
|
cnt: make(chan struct{}, 100),
|
||||||
|
curr: 0, // to display
|
||||||
|
total: 0, // sum of frames
|
||||||
|
frame: 0, // current frame
|
||||||
|
frames: make([]int, 20), // all frames
|
||||||
|
i: 0, // frame index
|
||||||
|
}
|
||||||
|
go fps.count()
|
||||||
|
return fps
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FPS) count() {
|
||||||
|
ticker := time.NewTicker(50 * time.Millisecond)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-f.done:
|
||||||
|
return
|
||||||
|
case <-f.cnt:
|
||||||
|
f.frame++
|
||||||
|
case <-ticker.C:
|
||||||
|
f.total -= f.frames[f.i]
|
||||||
|
f.frames[f.i] = f.frame
|
||||||
|
f.total += f.frames[f.i]
|
||||||
|
f.frame = 0
|
||||||
|
f.i = (f.i + 1) % len(f.frames)
|
||||||
|
f.curr = f.total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FPS) Count() {
|
||||||
|
f.cnt <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FPS) Current() int {
|
||||||
|
return f.curr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FPS) Destroy() {
|
||||||
|
close(f.done)
|
||||||
|
}
|
142
cmd/krampus19/game.go
Normal file
142
cmd/krampus19/game.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"image/png"
|
||||||
|
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/fs/vfs"
|
||||||
|
"opslag.de/schobers/krampus19/alui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Game struct {
|
||||||
|
ctx *Context
|
||||||
|
ui *alui.UI
|
||||||
|
main alui.Container
|
||||||
|
|
||||||
|
info *alui.Overlay
|
||||||
|
scene *alui.Overlay
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) initUI(disp *allg5.Display, fps *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)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) loadBitmap(path, name string) error {
|
||||||
|
f, err := g.ctx.Resources.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
im, err := png.Decode(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bmp, err := allg5.NewBitmapFromImage(im, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
g.ctx.Bitmaps[name] = bmp
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) loadBitmaps(pathToName map[string]string) error {
|
||||||
|
for path, name := range pathToName {
|
||||||
|
err := g.loadBitmap(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) loadLevels(names ...string) error {
|
||||||
|
g.ctx.Levels = map[string]level{}
|
||||||
|
for _, name := range names {
|
||||||
|
fileName := fmt.Sprintf("levels/level%s.txt", name)
|
||||||
|
f, err := g.ctx.Resources.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
level, err := loadLevelAsset(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
g.ctx.Levels[name] = level
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) loadAssets() error {
|
||||||
|
err := g.loadBitmaps(map[string]string{
|
||||||
|
"basic_tile.png": "basic_tile",
|
||||||
|
"water_tile.png": "water_tile",
|
||||||
|
|
||||||
|
"main_character.png": "main_character",
|
||||||
|
"villain_character.png": "villain_character",
|
||||||
|
|
||||||
|
"brick.png": "brick",
|
||||||
|
"crate.png": "crate",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = g.loadLevels("1")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) PlayScene() *playScene {
|
||||||
|
return g.scene.Proxy.(*playScene)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Destroy() {
|
||||||
|
g.ui.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 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := g.loadAssets(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
scene := g.PlayScene()
|
||||||
|
scene.init(g.ctx)
|
||||||
|
scene.loadLevel("1")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Handle(e allg5.Event) {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *allg5.KeyDownEvent:
|
||||||
|
if e.KeyCode == allg5.KeyF3 {
|
||||||
|
g.info.Visible = !g.info.Visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.ui.Handle(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Render() {
|
||||||
|
g.ui.Render()
|
||||||
|
}
|
19
cmd/krampus19/info.go
Normal file
19
cmd/krampus19/info.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
"opslag.de/schobers/krampus19/alui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type info struct {
|
||||||
|
alui.ControlBase
|
||||||
|
|
||||||
|
fps *FPS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *info) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
|
ctx.Fonts.Get("default").Draw(4, 4, allg5.NewColor(0xff, 0xff, 0xff), allg5.AlignLeft, fmt.Sprintf("FPS: %d", i.fps.Current()))
|
||||||
|
}
|
87
cmd/krampus19/krampus19.go
Normal file
87
cmd/krampus19/krampus19.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
rice "github.com/GeertJohan/go.rice"
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/fs/ricefs"
|
||||||
|
"opslag.de/schobers/fs/vfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate rice embed-syso
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := run()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resources() (vfs.CopyDir, error) {
|
||||||
|
var embeddedFs = ricefs.NewFs(rice.MustFindBox("res"))
|
||||||
|
var osFs = afero.NewBasePathFs(afero.NewOsFs(), "./res")
|
||||||
|
return vfs.NewCopyDir(vfs.NewFallbackFs(osFs, embeddedFs))
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() error {
|
||||||
|
err := allg5.Init(allg5.InitAll)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
disp, err := allg5.NewDisplay(1440, 900, allg5.NewDisplayOptions{Maximized: false, Windowed: true, Resizable: true, Vsync: true})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer disp.Destroy()
|
||||||
|
|
||||||
|
eq, err := allg5.NewEventQueue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer eq.Destroy()
|
||||||
|
|
||||||
|
eq.RegisterDisplay(disp)
|
||||||
|
eq.RegisterKeyboard()
|
||||||
|
eq.RegisterMouse()
|
||||||
|
|
||||||
|
res, err := resources()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer res.Destroy()
|
||||||
|
|
||||||
|
fps := NewFPS()
|
||||||
|
defer fps.Destroy()
|
||||||
|
|
||||||
|
game := &Game{}
|
||||||
|
err = game.Init(disp, res, fps)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer game.Destroy()
|
||||||
|
|
||||||
|
back := allg5.NewColor(0x21, 0x21, 0x21)
|
||||||
|
for {
|
||||||
|
allg5.ClearToColor(back)
|
||||||
|
game.Render()
|
||||||
|
disp.Flip()
|
||||||
|
fps.Count()
|
||||||
|
|
||||||
|
e := eq.Get()
|
||||||
|
for e != nil {
|
||||||
|
switch e := e.(type) {
|
||||||
|
case *allg5.DisplayCloseEvent:
|
||||||
|
return nil
|
||||||
|
case *allg5.KeyDownEvent:
|
||||||
|
if e.KeyCode == allg5.KeyEscape {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
game.Handle(e)
|
||||||
|
e = eq.Get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
142
cmd/krampus19/level.go
Normal file
142
cmd/krampus19/level.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type entity byte
|
||||||
|
type tile byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
entityInvalid entity = entity(0)
|
||||||
|
entityNone = '_'
|
||||||
|
entityCharacter = '@'
|
||||||
|
entityVillain = 'X'
|
||||||
|
entityBrick = 'B'
|
||||||
|
entityCrate = 'C'
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e entity) IsValid() bool {
|
||||||
|
switch e {
|
||||||
|
case entityNone:
|
||||||
|
case entityCharacter:
|
||||||
|
case entityVillain:
|
||||||
|
case entityBrick:
|
||||||
|
case entityCrate:
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tileInvalid tile = tile(0)
|
||||||
|
tileNothing = '.'
|
||||||
|
tileBasic = '#'
|
||||||
|
tileWater = '~'
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t tile) IsValid() bool {
|
||||||
|
switch t {
|
||||||
|
case tileNothing:
|
||||||
|
case tileBasic:
|
||||||
|
case tileWater:
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type level struct {
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
tiles []tile
|
||||||
|
entities []entity
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadLevelAsset(r io.Reader) (level, error) {
|
||||||
|
var l level
|
||||||
|
ctx := levelContext{&l, nil}
|
||||||
|
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 {
|
||||||
|
if p.eof() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch p.peek() {
|
||||||
|
case "level:":
|
||||||
|
return c.parseContent
|
||||||
|
case "":
|
||||||
|
p.next() // skip
|
||||||
|
return c.parse
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *levelContext) parseContent(p *lineParser) parseLineFn {
|
||||||
|
if p.next() != "level:" {
|
||||||
|
return c.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"))
|
||||||
|
}
|
||||||
|
line := p.next()
|
||||||
|
if line == ":level" {
|
||||||
|
return c.parse
|
||||||
|
}
|
||||||
|
if c.level.height == 0 {
|
||||||
|
c.level.width = len(line) / 2
|
||||||
|
}
|
||||||
|
return c.addRow(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *levelContext) addRow(line string) parseLineFn {
|
||||||
|
var tiles []tile
|
||||||
|
var entities []entity
|
||||||
|
for i := 0; i < len(line); i += 2 {
|
||||||
|
tiles = append(tiles, tile(line[i]))
|
||||||
|
entities = append(entities, entity(line[i+1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, t := range tiles {
|
||||||
|
if !t.IsValid() {
|
||||||
|
return c.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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.level.height++
|
||||||
|
c.level.tiles = append(c.level.tiles, tiles...)
|
||||||
|
c.level.entities = append(c.level.entities, entities...)
|
||||||
|
|
||||||
|
return c.parseRow
|
||||||
|
}
|
39
cmd/krampus19/lineparser.go
Normal file
39
cmd/krampus19/lineparser.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lineParser struct {
|
||||||
|
lines []string
|
||||||
|
i int
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
i := p.i
|
||||||
|
p.i++
|
||||||
|
return p.lines[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
type parseLineFn func(p *lineParser) parseLineFn
|
||||||
|
|
||||||
|
func parseLines(r io.Reader, fn parseLineFn) error {
|
||||||
|
content, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
for i, line := range lines {
|
||||||
|
lines[i] = strings.TrimRight(line, "\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
parser := &lineParser{lines: lines}
|
||||||
|
for fn != nil {
|
||||||
|
fn = fn(parser)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
3
cmd/krampus19/mainmenu.go
Normal file
3
cmd/krampus19/mainmenu.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type mainMenu struct{}
|
122
cmd/krampus19/playscene.go
Normal file
122
cmd/krampus19/playscene.go
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
"opslag.de/schobers/krampus19/alui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type playScene struct {
|
||||||
|
alui.ControlBase
|
||||||
|
|
||||||
|
ctx *Context
|
||||||
|
level level
|
||||||
|
|
||||||
|
offset geom.PointF32
|
||||||
|
scale float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *playScene) init(ctx *Context) {
|
||||||
|
s.ctx = ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *playScene) loadLevel(name string) {
|
||||||
|
s.level = s.ctx.Levels[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *playScene) 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)
|
||||||
|
return pos.Add(s.offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// <- 168->
|
||||||
|
// <-128->
|
||||||
|
//
|
||||||
|
// /----/| ^ ^
|
||||||
|
// / / / | 72
|
||||||
|
// /----/ / 92 v ^
|
||||||
|
// |----|/ v v 20
|
||||||
|
//
|
||||||
|
// Gap: 20
|
||||||
|
// Center: 84,46
|
||||||
|
// Offset between horizontal tiles: 148,0
|
||||||
|
// Offset between vertical tiles: -46,82
|
||||||
|
|
||||||
|
func (s *playScene) 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))
|
||||||
|
tilesCenter = geom.PtF32(tilesCenter.X*148-tilesCenter.Y*46, tilesCenter.Y*82)
|
||||||
|
tilesCenter = geom.PtF32(tilesCenter.X*s.scale, tilesCenter.Y*s.scale)
|
||||||
|
center := bounds.Center()
|
||||||
|
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 {
|
||||||
|
switch t {
|
||||||
|
case tileBasic:
|
||||||
|
return basicTile
|
||||||
|
case tileWater:
|
||||||
|
return waterTile
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
switch e {
|
||||||
|
case entityCharacter:
|
||||||
|
return character
|
||||||
|
case entityVillain:
|
||||||
|
return villain
|
||||||
|
case entityBrick:
|
||||||
|
return brick
|
||||||
|
case entityCrate:
|
||||||
|
return crate
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scale := 168 / float32(basicTile.Width())
|
||||||
|
|
||||||
|
// center := disp.Center()
|
||||||
|
|
||||||
|
level := s.level
|
||||||
|
for i, t := range level.tiles {
|
||||||
|
tile := tileBmp(t)
|
||||||
|
if tile == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pos := geom.Pt(i%level.width, i/level.width)
|
||||||
|
screenPos := s.toScreenPos(pos)
|
||||||
|
if t == tileWater {
|
||||||
|
screenPos.Y += 8 * s.scale
|
||||||
|
}
|
||||||
|
tile.DrawOptions(screenPos.X, screenPos.Y, allg5.DrawOptions{Center: true, Scale: allg5.NewUniformScale(s.scale * scale)})
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, e := range level.entities {
|
||||||
|
bmp := entityBmp(e)
|
||||||
|
if bmp == 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
|
||||||
|
}
|
||||||
|
bmp.DrawOptions(screenPos.X, screenPos.Y, allg5.DrawOptions{Center: true, Scale: allg5.NewUniformScale(s.scale * scale)})
|
||||||
|
}
|
||||||
|
}
|
BIN
cmd/krampus19/res/OpenSans-Regular.ttf
Normal file
BIN
cmd/krampus19/res/OpenSans-Regular.ttf
Normal file
Binary file not shown.
BIN
cmd/krampus19/res/basic_tile.png
Normal file
BIN
cmd/krampus19/res/basic_tile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.6 KiB |
BIN
cmd/krampus19/res/brick.png
Normal file
BIN
cmd/krampus19/res/brick.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
cmd/krampus19/res/crate.png
Normal file
BIN
cmd/krampus19/res/crate.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
11
cmd/krampus19/res/levels/level1.txt
Normal file
11
cmd/krampus19/res/levels/level1.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
level:
|
||||||
|
._._._._._._._._._._
|
||||||
|
._._#_#_#_._._._._._
|
||||||
|
._._#_._#_._._._._._
|
||||||
|
._._#_#_#_._._._._._
|
||||||
|
._._#_._#B._._._._._
|
||||||
|
._#@#_#_#_~_~_#_#X._
|
||||||
|
._._._._~_~_~_#_._._
|
||||||
|
._._._#_#_#_#_#_._._
|
||||||
|
._._._._._._._._._._
|
||||||
|
:level
|
BIN
cmd/krampus19/res/main_character.png
Normal file
BIN
cmd/krampus19/res/main_character.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
cmd/krampus19/res/villain_character.png
Normal file
BIN
cmd/krampus19/res/villain_character.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
cmd/krampus19/res/water_tile.png
Normal file
BIN
cmd/krampus19/res/water_tile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.0 KiB |
7
cmd/krampus19/splash.go
Normal file
7
cmd/krampus19/splash.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "opslag.de/schobers/krampus19/alui"
|
||||||
|
|
||||||
|
type splash struct {
|
||||||
|
alui.Container
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user