Added simple main menu.
Refactored some structs from public to internal. Separated navigation from game. Added utility methods for drawing text. Stackpanel will uses all available width when layouting.
This commit is contained in:
parent
a6cb582254
commit
0cd5cb4ad1
@ -11,21 +11,21 @@ type Button struct {
|
|||||||
ControlBase
|
ControlBase
|
||||||
|
|
||||||
Text string
|
Text string
|
||||||
|
TextAlign allg5.HorizontalAlignment
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Button) DesiredSize(ctx *Context) geom.PointF32 {
|
func (b *Button) DesiredSize(ctx *Context) geom.PointF32 {
|
||||||
font := ctx.Fonts.Get(b.Font)
|
font := ctx.Fonts.Get(b.Font)
|
||||||
_, _, w, h := font.TextDimensions(b.Text)
|
w := font.TextWidth(b.Text)
|
||||||
return geom.PtF32(w+8, h+8)
|
return geom.PtF32(w+8, font.Height()+8)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Button) Render(ctx *Context, bounds geom.RectangleF32) {
|
func (b *Button) Render(ctx *Context, bounds geom.RectangleF32) {
|
||||||
font := ctx.Fonts.Get(b.Font)
|
|
||||||
|
|
||||||
fore := ctx.Palette.Primary
|
fore := ctx.Palette.Primary
|
||||||
if b.Over {
|
if b.Over {
|
||||||
fore = ctx.Palette.Dark
|
fore = ctx.Palette.Dark
|
||||||
ctx.Cursor = allg5.MouseCursorLink
|
ctx.Cursor = allg5.MouseCursorLink
|
||||||
}
|
}
|
||||||
font.Draw(bounds.Min.X+4, bounds.Min.Y+4, fore, allg5.AlignLeft, b.Text)
|
font := ctx.Fonts.Get(b.Font)
|
||||||
|
ctx.Fonts.DrawAlignFont(font, bounds.Min.X+4, bounds.Min.Y+4, bounds.Max.X-4, fore, b.TextAlign, b.Text)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package alui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Fonts struct {
|
type Fonts struct {
|
||||||
@ -25,6 +26,41 @@ func (f *Fonts) Destroy() {
|
|||||||
f.fonts = nil
|
f.fonts = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) Draw(font string, left, top float32, color allg5.Color, text string) {
|
||||||
|
f.DrawFont(f.Get(font), left, top, color, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) DrawAlign(font string, left, top, right float32, color allg5.Color, align allg5.HorizontalAlignment, text string) {
|
||||||
|
f.DrawAlignFont(f.Get(font), left, top, right, color, align, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) DrawAlignFont(font *allg5.Font, left, top, right float32, color allg5.Color, align allg5.HorizontalAlignment, text string) {
|
||||||
|
switch align {
|
||||||
|
case allg5.AlignCenter:
|
||||||
|
center, top := geom.Round32(.5*(left+right)), geom.Round32(top)
|
||||||
|
font.Draw(center, top, color, allg5.AlignCenter, text)
|
||||||
|
case allg5.AlignRight:
|
||||||
|
right, top = geom.Round32(right), geom.Round32(top)
|
||||||
|
font.Draw(right, top, color, allg5.AlignRight, text)
|
||||||
|
default:
|
||||||
|
left, top = geom.Round32(left), geom.Round32(top)
|
||||||
|
font.Draw(left, top, color, allg5.AlignRight, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) DrawCenter(font string, center, top float32, color allg5.Color, text string) {
|
||||||
|
f.DrawCenterFont(f.Get(font), center, top, color, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) DrawCenterFont(font *allg5.Font, center, top float32, color allg5.Color, text string) {
|
||||||
|
f.DrawAlignFont(font, center, top, center, color, allg5.AlignCenter, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Fonts) DrawFont(font *allg5.Font, left, top float32, color allg5.Color, text string) {
|
||||||
|
left, top = geom.Round32(left), geom.Round32(top)
|
||||||
|
font.Draw(left, top, color, allg5.AlignLeft, text)
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Fonts) Len() int { return len(f.fonts) }
|
func (f *Fonts) Len() int { return len(f.fonts) }
|
||||||
|
|
||||||
func (f *Fonts) Load(path string, size int, name string) error {
|
func (f *Fonts) Load(path string, size int, name string) error {
|
||||||
|
@ -20,8 +20,6 @@ func (l *Label) DesiredSize(ctx *Context) geom.PointF32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *Label) Render(ctx *Context, bounds geom.RectangleF32) {
|
func (l *Label) Render(ctx *Context, bounds geom.RectangleF32) {
|
||||||
font := ctx.Fonts.Get(l.Font)
|
|
||||||
|
|
||||||
back := l.Background
|
back := l.Background
|
||||||
fore := l.Foreground
|
fore := l.Foreground
|
||||||
if fore == nil {
|
if fore == nil {
|
||||||
@ -31,5 +29,5 @@ func (l *Label) Render(ctx *Context, bounds geom.RectangleF32) {
|
|||||||
if back != nil {
|
if back != nil {
|
||||||
allg5.DrawFilledRectangle(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, *back)
|
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)
|
ctx.Fonts.Draw(l.Font, bounds.Min.X+4, bounds.Min.Y+4, *fore, l.Text)
|
||||||
}
|
}
|
||||||
|
@ -78,8 +78,8 @@ func (s *StackPanel) DesiredSize(ctx *Context) geom.PointF32 {
|
|||||||
func (s *StackPanel) Layout(ctx *Context, bounds geom.RectangleF32) {
|
func (s *StackPanel) Layout(ctx *Context, bounds geom.RectangleF32) {
|
||||||
s.Container.Layout(ctx, bounds)
|
s.Container.Layout(ctx, bounds)
|
||||||
|
|
||||||
desired, size := s.CalculateLayout(ctx)
|
desired, _ := s.CalculateLayout(ctx)
|
||||||
width := s.asWidth(size)
|
width := s.asWidth(bounds.Size())
|
||||||
|
|
||||||
var offset = bounds.Min
|
var offset = bounds.Min
|
||||||
for i, child := range s.Children {
|
for i, child := range s.Children {
|
||||||
|
@ -27,7 +27,6 @@ func newConsole(cons *gut.Console) *console {
|
|||||||
func (c *console) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
func (c *console) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
back := allg5.NewColorAlpha(0, 0, 0, 0x7f)
|
back := allg5.NewColorAlpha(0, 0, 0, 0x7f)
|
||||||
line := allg5.NewColor(0x7f, 0x7f, 0x7f)
|
line := allg5.NewColor(0x7f, 0x7f, 0x7f)
|
||||||
fore := allg5.NewColor(0xff, 0xff, 0xff)
|
|
||||||
|
|
||||||
c.height = geom.Min32(c.height, bounds.Dy())
|
c.height = geom.Min32(c.height, bounds.Dy())
|
||||||
top := geom.Max32(bounds.Max.Y-c.height, bounds.Min.Y)
|
top := geom.Max32(bounds.Max.Y-c.height, bounds.Min.Y)
|
||||||
@ -65,7 +64,7 @@ func (c *console) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
|||||||
messageTop := size.Y - totalHeight - c.offset
|
messageTop := size.Y - totalHeight - c.offset
|
||||||
for _, m := range messages {
|
for _, m := range messages {
|
||||||
if messageTop <= size.Y || (messageTop+lineHeight) >= 0 {
|
if messageTop <= size.Y || (messageTop+lineHeight) >= 0 {
|
||||||
font.Draw(0, messageTop, fore, allg5.AlignLeft, m)
|
ctx.Fonts.DrawFont(font, 0, messageTop, ctx.Palette.Text, m)
|
||||||
}
|
}
|
||||||
messageTop += lineHeight
|
messageTop += lineHeight
|
||||||
}
|
}
|
||||||
|
@ -3,28 +3,32 @@ package main
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"opslag.de/schobers/krampus19/alui"
|
||||||
|
|
||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
"opslag.de/schobers/fs/vfs"
|
"opslag.de/schobers/fs/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Texture struct {
|
type texture struct {
|
||||||
*allg5.Bitmap
|
*allg5.Bitmap
|
||||||
|
|
||||||
Subs map[string]*allg5.Bitmap
|
Subs map[string]*allg5.Bitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTexture(bmp *allg5.Bitmap) Texture {
|
func newTexture(bmp *allg5.Bitmap) texture {
|
||||||
return Texture{Bitmap: bmp, Subs: map[string]*allg5.Bitmap{}}
|
return texture{Bitmap: bmp, Subs: map[string]*allg5.Bitmap{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Resources vfs.CopyDir
|
Resources vfs.CopyDir
|
||||||
Textures map[string]Texture
|
Textures map[string]texture
|
||||||
Levels map[string]level
|
Levels map[string]level
|
||||||
Sprites map[string]sprite
|
Sprites map[string]sprite
|
||||||
Settings Settings
|
Settings settings
|
||||||
|
Palette *alui.Palette
|
||||||
|
|
||||||
Tick time.Duration
|
Tick time.Duration
|
||||||
|
Navigation navigation
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Destroy() {
|
func (c *Context) Destroy() {
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"opslag.de/schobers/krampus19/gut"
|
"opslag.de/schobers/krampus19/gut"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Game struct {
|
type game struct {
|
||||||
ctx *Context
|
ctx *Context
|
||||||
ui *alui.UI
|
ui *alui.UI
|
||||||
main alui.Container
|
main alui.Container
|
||||||
@ -22,10 +22,17 @@ type Game struct {
|
|||||||
scene *alui.Overlay
|
scene *alui.Overlay
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) initUI(disp *allg5.Display, cons *gut.Console, fps *gut.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)
|
||||||
|
ui.SetPalette(alui.Palette{
|
||||||
|
Icon: allg5.NewColor(0x21, 0x21, 0x21),
|
||||||
|
Dark: allg5.NewColor(0xf7, 0xd1, 0x56),
|
||||||
|
Primary: allg5.NewColor(0xf7, 0xbd, 0x00),
|
||||||
|
Text: allg5.NewColor(0xff, 0xff, 0xff),
|
||||||
|
})
|
||||||
g.ui = ui
|
g.ui = ui
|
||||||
|
g.ctx.Palette = &g.ui.Context().Palette
|
||||||
g.info = &alui.Overlay{Proxy: &info{fps: fps}}
|
g.info = &alui.Overlay{Proxy: &info{fps: fps}}
|
||||||
g.cons = &alui.Overlay{Proxy: newConsole(cons)}
|
g.cons = &alui.Overlay{Proxy: newConsole(cons)}
|
||||||
g.scene = &alui.Overlay{Proxy: &splash{}, Visible: true}
|
g.scene = &alui.Overlay{Proxy: &splash{}, Visible: true}
|
||||||
@ -33,7 +40,7 @@ func (g *Game) initUI(disp *allg5.Display, cons *gut.Console, fps *gut.FPS) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) loadBitmap(path, name string) error {
|
func (g *game) loadBitmap(path, name string) error {
|
||||||
f, err := g.ctx.Resources.Open(path)
|
f, err := g.ctx.Resources.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -47,11 +54,11 @@ func (g *Game) loadBitmap(path, name string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
g.ctx.Textures[name] = NewTexture(bmp)
|
g.ctx.Textures[name] = newTexture(bmp)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) loadFonts() error {
|
func (g *game) loadFonts() error {
|
||||||
openSansPath, err := g.ctx.Resources.Retrieve("fonts/OpenSans-Regular.ttf")
|
openSansPath, err := g.ctx.Resources.Retrieve("fonts/OpenSans-Regular.ttf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -60,11 +67,7 @@ func (g *Game) loadFonts() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = g.ui.Fonts().LoadFonts(alui.FontDescription{Path: openSansPath, Name: "default", Size: 12})
|
err = g.ui.Fonts().LoadFonts(alui.FontDescription{Path: openSansPath, Name: "default", Size: 36})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = g.ui.Fonts().LoadFonts(alui.FontDescription{Path: openSansPath, Name: "steps", Size: 36})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -75,7 +78,7 @@ func (g *Game) loadFonts() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) loadTextures(pathToName map[string]string) error {
|
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 {
|
||||||
@ -85,7 +88,7 @@ func (g *Game) loadTextures(pathToName map[string]string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) loadLevels(names ...string) error {
|
func (g *game) loadLevels(names ...string) error {
|
||||||
g.ctx.Levels = map[string]level{}
|
g.ctx.Levels = map[string]level{}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
fileName := fmt.Sprintf("levels/level%s.txt", name)
|
fileName := fmt.Sprintf("levels/level%s.txt", name)
|
||||||
@ -103,7 +106,7 @@ func (g *Game) loadLevels(names ...string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) loadSprites(names ...string) error {
|
func (g *game) loadSprites(names ...string) error {
|
||||||
g.ctx.Sprites = map[string]sprite{}
|
g.ctx.Sprites = map[string]sprite{}
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
fileName := fmt.Sprintf("sprites/%s.txt", name)
|
fileName := fmt.Sprintf("sprites/%s.txt", name)
|
||||||
@ -130,7 +133,7 @@ func (g *Game) loadSprites(names ...string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) loadAssets() error {
|
func (g *game) loadAssets() error {
|
||||||
log.Println("Loading textures...")
|
log.Println("Loading textures...")
|
||||||
err := g.loadTextures(map[string]string{
|
err := g.loadTextures(map[string]string{
|
||||||
"basic_tile.png": "basic_tile",
|
"basic_tile.png": "basic_tile",
|
||||||
@ -171,15 +174,14 @@ func (g *Game) loadAssets() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Destroy() {
|
func (g *game) Destroy() {
|
||||||
g.ui.Destroy()
|
g.ui.Destroy()
|
||||||
g.ctx.Destroy()
|
g.ctx.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Init(disp *allg5.Display, res vfs.CopyDir, cons *gut.Console, fps *gut.FPS) error {
|
func (g *game) Init(disp *allg5.Display, res vfs.CopyDir, cons *gut.Console, fps *gut.FPS) error {
|
||||||
log.Print("Initializing game...")
|
log.Print("Initializing game...")
|
||||||
g.ctx = &Context{Resources: res, Textures: map[string]Texture{}}
|
g.ctx = &Context{Resources: res, Textures: map[string]texture{}, Settings: newDefaultSettings(), Navigation: navigation{game: g}}
|
||||||
g.ctx.Settings = newDefaultSettings()
|
|
||||||
if err := g.initUI(disp, cons, fps); err != nil {
|
if err := g.initUI(disp, cons, fps); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -188,11 +190,13 @@ func (g *Game) Init(disp *allg5.Display, res vfs.CopyDir, cons *gut.Console, fps
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Print("Loaded assets.")
|
log.Print("Loaded assets.")
|
||||||
g.playLevel("1")
|
|
||||||
|
g.ctx.Navigation.showMainMenu()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) Handle(e allg5.Event) {
|
func (g *game) Handle(e allg5.Event) {
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *allg5.KeyDownEvent:
|
case *allg5.KeyDownEvent:
|
||||||
if e.KeyCode == allg5.KeyF3 {
|
if e.KeyCode == allg5.KeyF3 {
|
||||||
@ -205,18 +209,4 @@ func (g *Game) Handle(e allg5.Event) {
|
|||||||
g.ui.Handle(e)
|
g.ui.Handle(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) playLevel(l string) {
|
func (g *game) Render() { g.ui.Render() }
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"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"
|
"opslag.de/schobers/krampus19/gut"
|
||||||
@ -16,5 +15,5 @@ type info struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *info) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
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()))
|
ctx.Fonts.Draw("console", 4, 4, ctx.Palette.Text, fmt.Sprintf("FPS: %d", i.fps.Current()))
|
||||||
}
|
}
|
||||||
|
@ -29,16 +29,17 @@ func resources() (vfs.CopyDir, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func run() error {
|
func run() error {
|
||||||
|
defer log.Printf("All cleaned up, bye bye!")
|
||||||
cons := &gut.Console{}
|
cons := &gut.Console{}
|
||||||
log.SetOutput(io.MultiWriter(log.Writer(), cons))
|
log.SetOutput(io.MultiWriter(log.Writer(), cons))
|
||||||
|
|
||||||
log.Println("Initializing Allegro")
|
log.Printf("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")
|
log.Printf("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
|
||||||
@ -64,20 +65,20 @@ func run() error {
|
|||||||
fps := gut.NewFPS()
|
fps := gut.NewFPS()
|
||||||
defer fps.Destroy()
|
defer fps.Destroy()
|
||||||
|
|
||||||
game := &Game{}
|
game := &game{}
|
||||||
err = game.Init(disp, res, cons, 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")
|
log.Printf("Starting game loop")
|
||||||
back := allg5.NewColor(0x21, 0x21, 0x21)
|
defer log.Printf("Stopped game loop, cleaning up")
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
for {
|
for {
|
||||||
game.ctx.Tick = time.Now().Sub(start)
|
game.ctx.Tick = time.Now().Sub(start)
|
||||||
|
|
||||||
allg5.ClearToColor(back)
|
allg5.ClearToColor(game.ctx.Palette.Icon)
|
||||||
game.Render()
|
game.Render()
|
||||||
disp.Flip()
|
disp.Flip()
|
||||||
fps.Count()
|
fps.Count()
|
||||||
@ -86,14 +87,21 @@ func run() error {
|
|||||||
for e != nil {
|
for e != nil {
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *allg5.DisplayCloseEvent:
|
case *allg5.DisplayCloseEvent:
|
||||||
|
log.Printf("Stopping game loop, user closed display")
|
||||||
return nil
|
return nil
|
||||||
case *allg5.KeyDownEvent:
|
case *allg5.KeyCharEvent:
|
||||||
if e.KeyCode == allg5.KeyEscape {
|
if e.KeyCode == allg5.KeyF4 && e.Modifiers&allg5.KeyModAlt != 0 {
|
||||||
|
log.Printf("Stopping game loop, user pressed Alt+F4")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
game.Handle(e)
|
game.Handle(e)
|
||||||
e = eq.Get()
|
e = eq.Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if game.scene.Proxy == nil {
|
||||||
|
log.Printf("Stopping game loop, user quit via an in-game option")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,41 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
type mainMenu struct{}
|
import (
|
||||||
|
"opslag.de/schobers/allg5"
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
"opslag.de/schobers/krampus19/alui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mainMenu struct {
|
||||||
|
alui.ControlBase
|
||||||
|
|
||||||
|
ctx *Context
|
||||||
|
|
||||||
|
buttons alui.StackPanel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mainMenu) newButton(text string, onClick func()) alui.Control {
|
||||||
|
button := &alui.Button{Text: text, TextAlign: allg5.AlignCenter}
|
||||||
|
button.OnClick = onClick
|
||||||
|
return button
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mainMenu) Enter(ctx *Context) error {
|
||||||
|
m.ctx = ctx
|
||||||
|
m.buttons.Orientation = alui.OrientationVertical
|
||||||
|
m.buttons.Children = append(m.buttons.Children, m.newButton("Play", func() { m.ctx.Navigation.playLevel("1") }))
|
||||||
|
m.buttons.Children = append(m.buttons.Children, m.newButton("Quit", func() { m.ctx.Navigation.quit() }))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mainMenu) Leave() {}
|
||||||
|
|
||||||
|
func (m *mainMenu) Handle(e allg5.Event) { m.buttons.Handle(e) }
|
||||||
|
|
||||||
|
func (m *mainMenu) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
|
buttonsHeight := m.buttons.DesiredSize(ctx).Y
|
||||||
|
width, center := bounds.Dx(), bounds.Center()
|
||||||
|
buttonsBounds := geom.RectF32(.25*width, center.Y-.5*buttonsHeight, .75*width, center.Y+.5*buttonsHeight)
|
||||||
|
m.buttons.Layout(ctx, buttonsBounds)
|
||||||
|
m.buttons.Render(ctx, buttonsBounds)
|
||||||
|
}
|
||||||
|
40
cmd/krampus19/navigation.go
Normal file
40
cmd/krampus19/navigation.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"opslag.de/schobers/krampus19/alui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type navigation struct {
|
||||||
|
game *game
|
||||||
|
curr scene
|
||||||
|
}
|
||||||
|
|
||||||
|
type scene interface {
|
||||||
|
alui.Control
|
||||||
|
|
||||||
|
Enter(ctx *Context) error
|
||||||
|
Leave()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *navigation) playLevel(l string) {
|
||||||
|
n.switchTo(&playLevel{name: l})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *navigation) quit() {
|
||||||
|
n.switchTo(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *navigation) showMainMenu() {
|
||||||
|
n.switchTo(&mainMenu{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *navigation) switchTo(s scene) {
|
||||||
|
if n.curr != nil {
|
||||||
|
n.curr.Leave()
|
||||||
|
}
|
||||||
|
n.curr = s
|
||||||
|
n.game.scene.Proxy = s
|
||||||
|
if n.curr != nil {
|
||||||
|
n.curr.Enter(n.game.ctx)
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ import (
|
|||||||
type playLevel struct {
|
type playLevel struct {
|
||||||
alui.ControlBase
|
alui.ControlBase
|
||||||
|
|
||||||
|
name string
|
||||||
ctx *Context
|
ctx *Context
|
||||||
offset geom.PointF32
|
offset geom.PointF32
|
||||||
scale float32
|
scale float32
|
||||||
@ -80,9 +81,9 @@ func (a *entityMoveAnimation) Animate(start, now time.Duration) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) idxToPos(i int) geom.PointF32 { return s.level.idxToPos(i).ToF32() }
|
func (l *playLevel) idxToPos(i int) geom.PointF32 { return l.level.idxToPos(i).ToF32() }
|
||||||
|
|
||||||
func (s *playLevel) isIdle() bool { return s.ani.Idle() }
|
func (l *playLevel) isIdle() bool { return l.ani.Idle() }
|
||||||
|
|
||||||
func findEntityAt(entities []*entity, pos geom.Point) *entity {
|
func findEntityAt(entities []*entity, pos geom.Point) *entity {
|
||||||
idx := findEntityIdx(entities, pos)
|
idx := findEntityIdx(entities, pos)
|
||||||
@ -101,48 +102,48 @@ func findEntityIdx(entities []*entity, pos geom.Point) int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) findEntityAt(pos geom.Point) *entity {
|
func (l *playLevel) findEntityAt(pos geom.Point) *entity {
|
||||||
if s.player.pos == pos {
|
if l.player.pos == pos {
|
||||||
return s.player
|
return l.player
|
||||||
}
|
}
|
||||||
brick := findEntityAt(s.bricks, pos)
|
brick := findEntityAt(l.bricks, pos)
|
||||||
if brick != nil {
|
if brick != nil {
|
||||||
return brick
|
return brick
|
||||||
}
|
}
|
||||||
return findEntityAt(s.sunken, pos)
|
return findEntityAt(l.sunken, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) checkTile(pos geom.Point, check func(pos geom.Point, idx int, t tile) bool) bool {
|
func (l *playLevel) checkTile(pos geom.Point, check func(pos geom.Point, idx int, t tile) bool) bool {
|
||||||
return s.checkTileNotFound(pos, check, false)
|
return l.checkTileNotFound(pos, check, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) checkTileNotFound(pos geom.Point, check func(pos geom.Point, idx int, t tile) bool, notFound bool) bool {
|
func (l *playLevel) checkTileNotFound(pos geom.Point, check func(pos geom.Point, idx int, t tile) bool, notFound bool) bool {
|
||||||
idx := s.level.posToIdx(pos)
|
idx := l.level.posToIdx(pos)
|
||||||
if idx == -1 {
|
if idx == -1 {
|
||||||
return notFound
|
return notFound
|
||||||
}
|
}
|
||||||
return check(pos, idx, s.level.tiles[idx])
|
return check(pos, idx, l.level.tiles[idx])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) isSolidTile(pos geom.Point, idx int, t tile) bool {
|
func (l *playLevel) isSolidTile(pos geom.Point, idx int, t tile) bool {
|
||||||
switch t {
|
switch t {
|
||||||
case tileBasic:
|
case tileBasic:
|
||||||
return true
|
return true
|
||||||
case tileWater:
|
case tileWater:
|
||||||
return findEntityAt(s.sunken, pos) != nil
|
return findEntityAt(l.sunken, pos) != nil
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) wouldBrickSink(pos geom.Point, idx int, t tile) bool {
|
func (l *playLevel) wouldBrickSink(pos geom.Point, idx int, t tile) bool {
|
||||||
return t == tileWater && findEntityAt(s.sunken, pos) == nil
|
return t == tileWater && findEntityAt(l.sunken, pos) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) isObstructed(pos geom.Point, idx int, t tile) bool {
|
func (l *playLevel) isObstructed(pos geom.Point, idx int, t tile) bool {
|
||||||
if findEntityAt(s.bricks, pos) != nil {
|
if findEntityAt(l.bricks, pos) != nil {
|
||||||
return true // brick
|
return true // brick
|
||||||
}
|
}
|
||||||
switch s.level.tiles[idx] {
|
switch l.level.tiles[idx] {
|
||||||
case tileWater:
|
case tileWater:
|
||||||
return false
|
return false
|
||||||
case tileBasic:
|
case tileBasic:
|
||||||
@ -151,45 +152,50 @@ func (s *playLevel) isObstructed(pos geom.Point, idx int, t tile) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) canMove(from, dir geom.Point) bool {
|
func (l *playLevel) canMove(from, dir geom.Point) bool {
|
||||||
to := from.Add(dir)
|
to := from.Add(dir)
|
||||||
if !s.checkTile(to, s.isSolidTile) {
|
if !l.checkTile(to, l.isSolidTile) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
brick := findEntityAt(s.bricks, to)
|
brick := findEntityAt(l.bricks, to)
|
||||||
if brick != nil {
|
if brick != nil {
|
||||||
brickTo := to.Add(dir)
|
brickTo := to.Add(dir)
|
||||||
return !s.checkTileNotFound(brickTo, s.isObstructed, true)
|
return !l.checkTileNotFound(brickTo, l.isObstructed, true)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) loadLevel(name string) {
|
func (l *playLevel) Enter(ctx *Context) error {
|
||||||
s.keysDown = keyPressedState{}
|
l.ctx = ctx
|
||||||
s.level = s.ctx.Levels[name]
|
|
||||||
s.bricks = nil
|
l.keysDown = keyPressedState{}
|
||||||
s.sunken = nil
|
l.level = l.ctx.Levels[l.name]
|
||||||
for i, e := range s.level.entities {
|
l.bricks = nil
|
||||||
|
l.sunken = nil
|
||||||
|
for i, e := range l.level.entities {
|
||||||
switch e {
|
switch e {
|
||||||
case entityTypeBrick:
|
case entityTypeBrick:
|
||||||
s.bricks = append(s.bricks, newEntity(e, s.level.idxToPos(i)))
|
l.bricks = append(l.bricks, newEntity(e, l.level.idxToPos(i)))
|
||||||
case entityTypeCharacter:
|
case entityTypeCharacter:
|
||||||
s.player = newEntity(e, s.level.idxToPos(i))
|
l.player = newEntity(e, l.level.idxToPos(i))
|
||||||
case entityTypeVillain:
|
case entityTypeVillain:
|
||||||
s.villain = newEntity(e, s.level.idxToPos(i))
|
l.villain = newEntity(e, l.level.idxToPos(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) posToScreen(p geom.Point) geom.PointF32 {
|
func (l *playLevel) Leave() {}
|
||||||
return s.posToScreenF32(p.ToF32())
|
|
||||||
|
func (l *playLevel) posToScreen(p geom.Point) geom.PointF32 {
|
||||||
|
return l.posToScreenF32(p.ToF32())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) posToScreenF32(p geom.PointF32) geom.PointF32 {
|
func (l *playLevel) posToScreenF32(p geom.PointF32) geom.PointF32 {
|
||||||
pos := p.Add2D(.5, .5)
|
pos := p.Add2D(.5, .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*l.scale, pos.Y*l.scale)
|
||||||
return pos.Add(s.offset)
|
return pos.Add(l.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// <- 168->
|
// <- 168->
|
||||||
@ -205,90 +211,90 @@ func (s *playLevel) posToScreenF32(p geom.PointF32) 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 *playLevel) Layout(ctx *alui.Context, bounds geom.RectangleF32) {
|
func (l *playLevel) Layout(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
s.scale = bounds.Dy() / 1080
|
l.scale = bounds.Dy() / 1080
|
||||||
|
|
||||||
tilesCenter := geom.PtF32(.5*float32(s.level.width), .5*float32(s.level.height))
|
tilesCenter := geom.PtF32(.5*float32(l.level.width), .5*float32(l.level.height))
|
||||||
tilesCenter = geom.PtF32(tilesCenter.X*148-tilesCenter.Y*46, tilesCenter.Y*82)
|
tilesCenter = geom.PtF32(tilesCenter.X*148-tilesCenter.Y*46, tilesCenter.Y*82)
|
||||||
tilesCenter = geom.PtF32(tilesCenter.X*s.scale, tilesCenter.Y*s.scale)
|
tilesCenter = geom.PtF32(tilesCenter.X*l.scale, tilesCenter.Y*l.scale)
|
||||||
center := bounds.Center()
|
center := bounds.Center()
|
||||||
s.offset = geom.PtF32(center.X-tilesCenter.X, center.Y-tilesCenter.Y)
|
l.offset = geom.PtF32(center.X-tilesCenter.X, center.Y-tilesCenter.Y)
|
||||||
|
|
||||||
s.ani.Animate(s.ctx.Tick)
|
l.ani.Animate(l.ctx.Tick)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) tryPlayerMove(dir geom.Point, key allg5.Key) {
|
func (l *playLevel) tryPlayerMove(dir geom.Point, key allg5.Key) {
|
||||||
if !s.isIdle() {
|
if !l.isIdle() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
to := s.player.pos.Add(dir)
|
to := l.player.pos.Add(dir)
|
||||||
if !s.canMove(s.player.pos, dir) {
|
if !l.canMove(l.player.pos, dir) {
|
||||||
log.Printf("Move is not allowed (tried out move to %s after key '%s' was pressed)", to, gut.KeyToString(key))
|
log.Printf("Move is not allowed (tried out move to %s after key '%s' was pressed)", to, gut.KeyToString(key))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.steps++
|
l.steps++
|
||||||
log.Printf("Moving player to %s", to)
|
log.Printf("Moving player to %s", to)
|
||||||
s.ani.StartFn(s.ctx.Tick, newEntityMoveAnimation(s.player, to), func() {
|
l.ani.StartFn(l.ctx.Tick, newEntityMoveAnimation(l.player, to), func() {
|
||||||
log.Printf("Player movement finished")
|
log.Printf("Player movement finished")
|
||||||
if s.keysDown[key] && s.keysDown.CountPressed(s.ctx.Settings.Controls.MovementKeys()...) == 1 {
|
if l.keysDown[key] && l.keysDown.CountPressed(l.ctx.Settings.Controls.MovementKeys()...) == 1 {
|
||||||
log.Printf("Key %s is still down, moving further", gut.KeyToString(key))
|
log.Printf("Key %s is still down, moving further", gut.KeyToString(key))
|
||||||
s.tryPlayerMove(dir, key)
|
l.tryPlayerMove(dir, key)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if brick := findEntityAt(s.bricks, to); brick != nil {
|
if brick := findEntityAt(l.bricks, to); brick != nil {
|
||||||
log.Printf("Pushing brick at %s", to)
|
log.Printf("Pushing brick at %s", to)
|
||||||
brickTo := to.Add(dir)
|
brickTo := to.Add(dir)
|
||||||
s.ani.StartFn(s.ctx.Tick, newEntityMoveAnimation(brick, brickTo), func() {
|
l.ani.StartFn(l.ctx.Tick, newEntityMoveAnimation(brick, brickTo), func() {
|
||||||
log.Printf("Brick movement finished")
|
log.Printf("Brick movement finished")
|
||||||
if s.checkTile(brickTo, s.wouldBrickSink) {
|
if l.checkTile(brickTo, l.wouldBrickSink) {
|
||||||
log.Printf("Sinking brick at %s", brickTo)
|
log.Printf("Sinking brick at %s", brickTo)
|
||||||
idx := findEntityIdx(s.bricks, brickTo)
|
idx := findEntityIdx(l.bricks, brickTo)
|
||||||
s.bricks = append(s.bricks[:idx], s.bricks[idx+1:]...)
|
l.bricks = append(l.bricks[:idx], l.bricks[idx+1:]...)
|
||||||
s.sunken = append(s.sunken, brick)
|
l.sunken = append(l.sunken, brick)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) Handle(e allg5.Event) {
|
func (l *playLevel) Handle(e allg5.Event) {
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *allg5.KeyDownEvent:
|
case *allg5.KeyDownEvent:
|
||||||
s.keysDown[e.KeyCode] = true
|
l.keysDown[e.KeyCode] = true
|
||||||
|
|
||||||
switch e.KeyCode {
|
switch e.KeyCode {
|
||||||
case s.ctx.Settings.Controls.MoveUp:
|
case l.ctx.Settings.Controls.MoveUp:
|
||||||
s.tryPlayerMove(geom.Pt(0, -1), e.KeyCode)
|
l.tryPlayerMove(geom.Pt(0, -1), e.KeyCode)
|
||||||
case s.ctx.Settings.Controls.MoveRight:
|
case l.ctx.Settings.Controls.MoveRight:
|
||||||
s.tryPlayerMove(geom.Pt(1, 0), e.KeyCode)
|
l.tryPlayerMove(geom.Pt(1, 0), e.KeyCode)
|
||||||
case s.ctx.Settings.Controls.MoveDown:
|
case l.ctx.Settings.Controls.MoveDown:
|
||||||
s.tryPlayerMove(geom.Pt(0, 1), e.KeyCode)
|
l.tryPlayerMove(geom.Pt(0, 1), e.KeyCode)
|
||||||
case s.ctx.Settings.Controls.MoveLeft:
|
case l.ctx.Settings.Controls.MoveLeft:
|
||||||
s.tryPlayerMove(geom.Pt(-1, 0), e.KeyCode)
|
l.tryPlayerMove(geom.Pt(-1, 0), e.KeyCode)
|
||||||
}
|
}
|
||||||
case *allg5.KeyUpEvent:
|
case *allg5.KeyUpEvent:
|
||||||
s.keysDown[e.KeyCode] = false
|
l.keysDown[e.KeyCode] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
||||||
basicTile := s.ctx.Textures["basic_tile"]
|
basicTile := l.ctx.Textures["basic_tile"]
|
||||||
waterTile := s.ctx.Textures["water_tile"]
|
waterTile := l.ctx.Textures["water_tile"]
|
||||||
sunkenBrickTile := s.ctx.Textures["sunken_brick_tile"]
|
sunkenBrickTile := l.ctx.Textures["sunken_brick_tile"]
|
||||||
|
|
||||||
opts := allg5.DrawOptions{Center: true, Scale: allg5.NewUniformScale(s.scale * (168 / float32(basicTile.Width())))}
|
opts := allg5.DrawOptions{Center: true, Scale: allg5.NewUniformScale(l.scale * (168 / float32(basicTile.Width())))}
|
||||||
level := s.level
|
level := l.level
|
||||||
for i, t := range level.tiles {
|
for i, t := range level.tiles {
|
||||||
pos := geom.Pt(i%level.width, i/level.width)
|
pos := geom.Pt(i%level.width, i/level.width)
|
||||||
scrPos := s.posToScreen(pos)
|
scrPos := l.posToScreen(pos)
|
||||||
switch t {
|
switch t {
|
||||||
case tileBasic:
|
case tileBasic:
|
||||||
basicTile.DrawOptions(scrPos.X, scrPos.Y, opts)
|
basicTile.DrawOptions(scrPos.X, scrPos.Y, opts)
|
||||||
case tileWater:
|
case tileWater:
|
||||||
scrPos := s.posToScreenF32(pos.ToF32().Add2D(0, .2*s.scale))
|
scrPos := l.posToScreenF32(pos.ToF32().Add2D(0, .2*l.scale))
|
||||||
if findEntityAt(s.sunken, pos) == nil {
|
if findEntityAt(l.sunken, pos) == nil {
|
||||||
waterTile.DrawOptions(scrPos.X, scrPos.Y, opts)
|
waterTile.DrawOptions(scrPos.X, scrPos.Y, opts)
|
||||||
} else {
|
} else {
|
||||||
sunkenBrickTile.DrawOptions(scrPos.X, scrPos.Y, opts)
|
sunkenBrickTile.DrawOptions(scrPos.X, scrPos.Y, opts)
|
||||||
@ -296,15 +302,15 @@ func (s *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
character := s.ctx.Textures["main_character"]
|
character := l.ctx.Textures["main_character"]
|
||||||
villain := s.ctx.Textures["villain_character"]
|
villain := l.ctx.Textures["villain_character"]
|
||||||
brick := s.ctx.Textures["brick"]
|
brick := l.ctx.Textures["brick"]
|
||||||
crate := s.ctx.Textures["crate"]
|
crate := l.ctx.Textures["crate"]
|
||||||
|
|
||||||
var entities []*entity
|
var entities []*entity
|
||||||
entities = append(entities, s.player)
|
entities = append(entities, l.player)
|
||||||
entities = append(entities, s.villain)
|
entities = append(entities, l.villain)
|
||||||
entities = append(entities, s.bricks...)
|
entities = append(entities, l.bricks...)
|
||||||
|
|
||||||
sort.Slice(entities, func(i, j int) bool {
|
sort.Slice(entities, func(i, j int) bool {
|
||||||
if entities[i].scr.Y == entities[j].scr.Y {
|
if entities[i].scr.Y == entities[j].scr.Y {
|
||||||
@ -314,27 +320,26 @@ func (s *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
for _, e := range entities {
|
for _, e := range entities {
|
||||||
scrPos := s.posToScreenF32(e.scr)
|
scrPos := l.posToScreenF32(e.scr)
|
||||||
switch e.typ {
|
switch e.typ {
|
||||||
case entityTypeCharacter:
|
case entityTypeCharacter:
|
||||||
scrPos := s.posToScreenF32(e.scr.Add2D(-.2*s.scale, -.9*s.scale))
|
scrPos := l.posToScreenF32(e.scr.Add2D(-.2*l.scale, -.9*l.scale))
|
||||||
character.DrawOptions(scrPos.X, scrPos.Y, opts)
|
character.DrawOptions(scrPos.X, scrPos.Y, opts)
|
||||||
case entityTypeVillain:
|
case entityTypeVillain:
|
||||||
scrPos := s.posToScreenF32(e.scr.Add2D(-.2*s.scale, -.9*s.scale))
|
scrPos := l.posToScreenF32(e.scr.Add2D(-.2*l.scale, -.9*l.scale))
|
||||||
villain.DrawOptions(scrPos.X, scrPos.Y, opts)
|
villain.DrawOptions(scrPos.X, scrPos.Y, opts)
|
||||||
case entityTypeBrick:
|
case entityTypeBrick:
|
||||||
if findEntityAt(s.bricks, e.pos) == nil {
|
if findEntityAt(l.bricks, e.pos) == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
scrPos := s.posToScreenF32(e.scr.Add2D(-.2*s.scale, -.7*s.scale))
|
scrPos := l.posToScreenF32(e.scr.Add2D(-.2*l.scale, -.7*l.scale))
|
||||||
brick.DrawOptions(scrPos.X, scrPos.Y, opts)
|
brick.DrawOptions(scrPos.X, scrPos.Y, opts)
|
||||||
case entityTypeCrate:
|
case entityTypeCrate:
|
||||||
crate.DrawOptions(scrPos.X, scrPos.Y, opts)
|
crate.DrawOptions(scrPos.X, scrPos.Y, opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
steps := fmt.Sprintf("STEPS: %d", s.steps)
|
font := ctx.Fonts.Get("default")
|
||||||
stepsFont := ctx.Fonts.Get("steps")
|
steps := fmt.Sprintf("STEPS: %d", l.steps)
|
||||||
stepsWidth := stepsFont.TextWidth(steps)
|
ctx.Fonts.DrawAlignFont(font, bounds.Min.X, 24, bounds.Max.X, ctx.Palette.Text, allg5.AlignCenter, steps)
|
||||||
stepsFont.Draw(.5*(bounds.Dx()-stepsWidth), 24, allg5.NewColor(255, 255, 255), allg5.AlignCenter, steps)
|
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,13 @@ package main
|
|||||||
|
|
||||||
import "opslag.de/schobers/allg5"
|
import "opslag.de/schobers/allg5"
|
||||||
|
|
||||||
type Settings struct {
|
type settings struct {
|
||||||
Controls Controls
|
Controls controls
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDefaultSettings() Settings {
|
func newDefaultSettings() settings {
|
||||||
return Settings{
|
return settings{
|
||||||
Controls: Controls{
|
Controls: controls{
|
||||||
MoveUp: allg5.KeyUp,
|
MoveUp: allg5.KeyUp,
|
||||||
MoveRight: allg5.KeyRight,
|
MoveRight: allg5.KeyRight,
|
||||||
MoveDown: allg5.KeyDown,
|
MoveDown: allg5.KeyDown,
|
||||||
@ -17,13 +17,13 @@ func newDefaultSettings() Settings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Controls struct {
|
type controls struct {
|
||||||
MoveUp allg5.Key
|
MoveUp allg5.Key
|
||||||
MoveRight allg5.Key
|
MoveRight allg5.Key
|
||||||
MoveDown allg5.Key
|
MoveDown allg5.Key
|
||||||
MoveLeft allg5.Key
|
MoveLeft allg5.Key
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Controls) MovementKeys() []allg5.Key {
|
func (c controls) MovementKeys() []allg5.Key {
|
||||||
return []allg5.Key{c.MoveUp, c.MoveRight, c.MoveDown, c.MoveLeft}
|
return []allg5.Key{c.MoveUp, c.MoveRight, c.MoveDown, c.MoveLeft}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user