Added win condition.
Renamed entities on disk & added missing sprites. Moved entity and animations to separate code unit.
This commit is contained in:
parent
aa47e2cabb
commit
356b510286
@ -21,10 +21,12 @@ func NewMargins(target Control, margins ...float32) *Margins {
|
|||||||
m.Top, m.Left, m.Bottom, m.Right = margins[0], margins[0], margins[0], margins[0]
|
m.Top, m.Left, m.Bottom, m.Right = margins[0], margins[0], margins[0], margins[0]
|
||||||
case 2:
|
case 2:
|
||||||
m.Top, m.Left, m.Bottom, m.Right = margins[0], margins[1], margins[0], margins[1]
|
m.Top, m.Left, m.Bottom, m.Right = margins[0], margins[1], margins[0], margins[1]
|
||||||
|
case 3:
|
||||||
|
m.Top, m.Left, m.Bottom, m.Right = margins[0], margins[1], margins[2], margins[1]
|
||||||
case 4:
|
case 4:
|
||||||
m.Top, m.Left, m.Bottom, m.Right = margins[0], margins[1], margins[2], margins[3]
|
m.Top, m.Left, m.Bottom, m.Right = margins[0], margins[1], margins[2], margins[3]
|
||||||
default:
|
default:
|
||||||
panic("expected 1 (all same), 2 (vertical, horizontal) or 4 margins (all separately specified)")
|
panic("expected 1 (all same), 2 (vertical, horizontal), 3 (top, horizontal, bottom) or 4 margins (all separately specified)")
|
||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ func newSettingsHeader(label string) alui.Control {
|
|||||||
header := &settingsHeader{}
|
header := &settingsHeader{}
|
||||||
header.Font = "header"
|
header.Font = "header"
|
||||||
header.Text = label
|
header.Text = label
|
||||||
return alui.NewMargins(header, 3*margin, 0, 2*margin, 0)
|
return alui.NewMargins(header, 3*margin, 0, 2*margin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type settingsRow struct {
|
type settingsRow struct {
|
||||||
|
42
cmd/krampus19/entity.go
Normal file
42
cmd/krampus19/entity.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
)
|
||||||
|
|
||||||
|
type entity struct {
|
||||||
|
typ entityType
|
||||||
|
pos geom.Point
|
||||||
|
scr geom.PointF32
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEntity(typ entityType, pos geom.Point) *entity {
|
||||||
|
return &entity{typ, pos, pos.ToF32()}
|
||||||
|
}
|
||||||
|
|
||||||
|
type entityMoveAnimation struct {
|
||||||
|
e *entity
|
||||||
|
from, to geom.Point
|
||||||
|
pos geom.PointF32
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEntityMoveAnimation(e *entity, to geom.Point) *entityMoveAnimation {
|
||||||
|
ani := &entityMoveAnimation{e: e, from: e.pos, to: to, pos: e.pos.ToF32()}
|
||||||
|
ani.e.pos = to
|
||||||
|
return ani
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *entityMoveAnimation) Animate(start, now time.Duration) bool {
|
||||||
|
const duration = 270 * time.Millisecond
|
||||||
|
|
||||||
|
progress := float32((now-start)*1000/duration) * .001
|
||||||
|
from, to := a.from.ToF32(), a.to.ToF32()
|
||||||
|
if progress >= 1 {
|
||||||
|
a.e.scr = to
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
a.e.scr = to.Sub(from).Mul(progress).Add(from)
|
||||||
|
return true
|
||||||
|
}
|
@ -150,7 +150,8 @@ 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{
|
||||||
"entity_brick.png": "brick",
|
"entity_brick.png": "brick",
|
||||||
"entity_main_character.png": "main_character",
|
"entity_dragon.png": "dragon",
|
||||||
|
"entity_villain.png": "villain",
|
||||||
|
|
||||||
"tile_lava_brick.png": "lava_brick",
|
"tile_lava_brick.png": "lava_brick",
|
||||||
"tile_magma.png": "magma",
|
"tile_magma.png": "magma",
|
||||||
@ -177,7 +178,7 @@ func (g *game) loadAssets() error {
|
|||||||
log.Printf("Loaded %d fonts.\n", g.ui.Fonts().Len())
|
log.Printf("Loaded %d fonts.\n", g.ui.Fonts().Len())
|
||||||
|
|
||||||
log.Println("Loading sprites")
|
log.Println("Loading sprites")
|
||||||
err = g.loadSprites("brick", "lava_brick", "magma", "main_character", "ui")
|
err = g.loadSprites("brick", "dragon", "lava_brick", "magma", "ui", "villain")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,23 @@ type levelPack struct {
|
|||||||
levels map[string]level
|
levels map[string]level
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p levelPack) find(level string) int {
|
||||||
|
for i, l := range p.order {
|
||||||
|
if l == level {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p levelPack) FindNext(level string) (string, bool) {
|
||||||
|
idx := p.find(level)
|
||||||
|
if idx == -1 || idx == len(p.order)-1 {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return p.order[idx+1], true
|
||||||
|
}
|
||||||
|
|
||||||
type parseLevelPackContext struct {
|
type parseLevelPackContext struct {
|
||||||
name string
|
name string
|
||||||
levels []string
|
levels []string
|
||||||
|
@ -21,7 +21,6 @@ func (s *levelSelect) Enter(ctx *Context) error {
|
|||||||
s.Init()
|
s.Init()
|
||||||
name := func(id string) string { return fmt.Sprintf("Level %s", id) }
|
name := func(id string) string { return fmt.Sprintf("Level %s", id) }
|
||||||
for _, id := range s.pack.order {
|
for _, id := range s.pack.order {
|
||||||
// level := s.pack[id]
|
|
||||||
levelID := id
|
levelID := id
|
||||||
s.Add(name(levelID), func() {
|
s.Add(name(levelID), func() {
|
||||||
s.ctx.Navigation.PlayLevel(s.packID, levelID)
|
s.ctx.Navigation.PlayLevel(s.packID, levelID)
|
||||||
|
@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
|
||||||
|
|
||||||
"opslag.de/schobers/allg5"
|
"opslag.de/schobers/allg5"
|
||||||
"opslag.de/schobers/geom"
|
"opslag.de/schobers/geom"
|
||||||
@ -17,6 +16,7 @@ type playLevel struct {
|
|||||||
init bool
|
init bool
|
||||||
|
|
||||||
menu *alui.Menu
|
menu *alui.Menu
|
||||||
|
end alui.Control
|
||||||
showMenu bool
|
showMenu bool
|
||||||
|
|
||||||
packID string
|
packID string
|
||||||
@ -39,43 +39,6 @@ func (s keyPressedState) CountPressed(keys ...allg5.Key) int {
|
|||||||
return cnt
|
return cnt
|
||||||
}
|
}
|
||||||
|
|
||||||
type entity struct {
|
|
||||||
typ entityType
|
|
||||||
pos geom.Point
|
|
||||||
scr geom.PointF32
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEntity(typ entityType, pos geom.Point) *entity {
|
|
||||||
return &entity{typ, pos, pos.ToF32()}
|
|
||||||
}
|
|
||||||
|
|
||||||
type posToScrFn func(geom.Point) geom.PointF32
|
|
||||||
|
|
||||||
type entityMoveAnimation struct {
|
|
||||||
e *entity
|
|
||||||
from, to geom.Point
|
|
||||||
pos geom.PointF32
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEntityMoveAnimation(e *entity, to geom.Point) *entityMoveAnimation {
|
|
||||||
ani := &entityMoveAnimation{e: e, from: e.pos, to: to, pos: e.pos.ToF32()}
|
|
||||||
ani.e.pos = to
|
|
||||||
return ani
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *entityMoveAnimation) Animate(start, now time.Duration) bool {
|
|
||||||
const duration = 210 * time.Millisecond
|
|
||||||
|
|
||||||
progress := float32((now-start)*1000/duration) * .001
|
|
||||||
from, to := a.from.ToF32(), a.to.ToF32()
|
|
||||||
if progress >= 1 {
|
|
||||||
a.e.scr = to
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
a.e.scr = to.Sub(from).Mul(progress).Add(from)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *playLevel) Enter(ctx *Context) error {
|
func (l *playLevel) Enter(ctx *Context) error {
|
||||||
l.ctx = ctx
|
l.ctx = ctx
|
||||||
|
|
||||||
@ -88,12 +51,27 @@ func (l *playLevel) Enter(ctx *Context) error {
|
|||||||
l.menu.OnEscape = func() { l.showMenu = false }
|
l.menu.OnEscape = func() { l.showMenu = false }
|
||||||
|
|
||||||
l.init = true
|
l.init = true
|
||||||
l.state.Init(l.ctx, l.packID, l.levelID)
|
l.state.Init(l.ctx, l.packID, l.levelID, l.onComplete)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *playLevel) Leave() {}
|
func (l *playLevel) Leave() {}
|
||||||
|
|
||||||
|
func (l *playLevel) onComplete() {
|
||||||
|
menu := alui.NewMenu()
|
||||||
|
menu.AddChild(alui.NewMargins(&alui.Label{ControlBase: alui.ControlBase{Font: "header"}, Text: "Congratulations", TextAlign: allg5.AlignCenter}, 3*margin, 0, 2*margin))
|
||||||
|
menu.AddChild(alui.NewMargins(&alui.Label{Text: fmt.Sprintf("You've completed the level in %d steps", l.state.steps), TextAlign: allg5.AlignCenter}, 0, 0, 2*margin))
|
||||||
|
|
||||||
|
nextID, ok := l.state.pack.FindNext(l.levelID)
|
||||||
|
if ok {
|
||||||
|
menu.Add("Continue with next", func() { l.ctx.Navigation.PlayLevel(l.packID, nextID) })
|
||||||
|
}
|
||||||
|
menu.Add("Select level", func() { l.ctx.Navigation.SelectLevel(l.packID) })
|
||||||
|
menu.Add("Go to main menu", func() { l.ctx.Navigation.ShowMainMenu() })
|
||||||
|
|
||||||
|
l.end = menu
|
||||||
|
}
|
||||||
|
|
||||||
func (l *playLevel) posToScreenF32(p geom.PointF32, z float32) geom.PointF32 {
|
func (l *playLevel) posToScreenF32(p geom.PointF32, z float32) geom.PointF32 {
|
||||||
pos := l.posToCabinet(p.Add2D(.5, .5)).Add2D(0, z)
|
pos := l.posToCabinet(p.Add2D(.5, .5)).Add2D(0, z)
|
||||||
return pos.Mul(l.scale).Add(l.offset)
|
return pos.Mul(l.scale).Add(l.offset)
|
||||||
@ -156,11 +134,12 @@ func (l *playLevel) Handle(e allg5.Event) {
|
|||||||
l.state.ReleaseKey(e.KeyCode)
|
l.state.ReleaseKey(e.KeyCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.showMenu {
|
switch {
|
||||||
|
case l.showMenu:
|
||||||
l.menu.Handle(e)
|
l.menu.Handle(e)
|
||||||
return
|
case l.state.complete:
|
||||||
}
|
l.end.Handle(e)
|
||||||
|
default:
|
||||||
switch e := e.(type) {
|
switch e := e.(type) {
|
||||||
case *allg5.KeyCharEvent:
|
case *allg5.KeyCharEvent:
|
||||||
switch e.KeyCode {
|
switch e.KeyCode {
|
||||||
@ -177,6 +156,7 @@ func (l *playLevel) Handle(e allg5.Event) {
|
|||||||
l.state.TryPlayerMove(geom.Pt(-1, 0), e.KeyCode)
|
l.state.TryPlayerMove(geom.Pt(-1, 0), e.KeyCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *playLevel) drawSprite(name, partName string, pos geom.PointF32) {
|
func (l *playLevel) drawSprite(name, partName string, pos geom.PointF32) {
|
||||||
@ -217,10 +197,12 @@ func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
|||||||
|
|
||||||
for _, e := range entities {
|
for _, e := range entities {
|
||||||
switch e.typ {
|
switch e.typ {
|
||||||
case entityTypeCharacter:
|
|
||||||
l.drawSprite("main_character", "main_character", e.scr)
|
|
||||||
case entityTypeBrick:
|
case entityTypeBrick:
|
||||||
l.drawSprite("brick", "brick", e.scr)
|
l.drawSprite("brick", "brick", e.scr)
|
||||||
|
case entityTypeCharacter:
|
||||||
|
l.drawSprite("dragon", "dragon", e.scr)
|
||||||
|
case entityTypeVillain:
|
||||||
|
l.drawSprite("villain", "villain", e.scr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,8 +210,12 @@ func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
|
|||||||
steps := fmt.Sprintf("STEPS: %d", l.state.Steps())
|
steps := fmt.Sprintf("STEPS: %d", l.state.Steps())
|
||||||
ctx.Fonts.DrawAlignFont(font, bounds.Min.X, 24, bounds.Max.X, ctx.Palette.Text, allg5.AlignCenter, steps)
|
ctx.Fonts.DrawAlignFont(font, bounds.Min.X, 24, bounds.Max.X, ctx.Palette.Text, allg5.AlignCenter, steps)
|
||||||
|
|
||||||
if l.showMenu {
|
switch {
|
||||||
|
case l.showMenu:
|
||||||
allg5.DrawFilledRectangle(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, allg5.NewColorAlpha(0, 0, 0, 0xaf))
|
allg5.DrawFilledRectangle(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, allg5.NewColorAlpha(0, 0, 0, 0xaf))
|
||||||
l.menu.Render(ctx, bounds)
|
l.menu.Render(ctx, bounds)
|
||||||
|
case l.state.complete:
|
||||||
|
allg5.DrawFilledRectangle(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, allg5.NewColorAlpha(0, 0, 0, 0xaf))
|
||||||
|
l.end.Render(ctx, bounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,11 @@ type playLevelState struct {
|
|||||||
villain *entity
|
villain *entity
|
||||||
bricks []*entity
|
bricks []*entity
|
||||||
sunken []*entity
|
sunken []*entity
|
||||||
|
|
||||||
steps int
|
steps int
|
||||||
|
complete bool
|
||||||
|
onComplete func()
|
||||||
|
|
||||||
tick time.Duration
|
tick time.Duration
|
||||||
ani gut.Animations
|
ani gut.Animations
|
||||||
keysDown keyPressedState
|
keysDown keyPressedState
|
||||||
@ -44,9 +48,7 @@ type playLevelState struct {
|
|||||||
func (s *playLevelState) Entities() []*entity {
|
func (s *playLevelState) Entities() []*entity {
|
||||||
var entities []*entity
|
var entities []*entity
|
||||||
entities = append(entities, s.player)
|
entities = append(entities, s.player)
|
||||||
if s.villain != nil {
|
|
||||||
entities = append(entities, s.villain)
|
entities = append(entities, s.villain)
|
||||||
}
|
|
||||||
entities = append(entities, s.bricks...)
|
entities = append(entities, s.bricks...)
|
||||||
return entities
|
return entities
|
||||||
}
|
}
|
||||||
@ -62,7 +64,7 @@ func (s *playLevelState) IsNextToMagma(pos geom.Point) bool {
|
|||||||
s.checkTile(pos.Add2D(0, 1), s.isMagma)
|
s.checkTile(pos.Add2D(0, 1), s.isMagma)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevelState) Init(ctx *Context, pack, level string) {
|
func (s *playLevelState) Init(ctx *Context, pack, level string, onComplete func()) {
|
||||||
s.ctx = ctx
|
s.ctx = ctx
|
||||||
s.pack = ctx.Levels[pack]
|
s.pack = ctx.Levels[pack]
|
||||||
s.level = s.pack.levels[level]
|
s.level = s.pack.levels[level]
|
||||||
@ -79,6 +81,7 @@ func (s *playLevelState) Init(ctx *Context, pack, level string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.keysDown = keyPressedState{}
|
s.keysDown = keyPressedState{}
|
||||||
|
s.onComplete = onComplete
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *playLevelState) Level() level { return s.level }
|
func (s *playLevelState) Level() level { return s.level }
|
||||||
@ -112,7 +115,12 @@ func (s *playLevelState) TryPlayerMove(dir geom.Point, key allg5.Key) {
|
|||||||
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() {
|
s.ani.StartFn(s.ctx.Tick, newEntityMoveAnimation(s.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 s.player.pos == s.villain.pos {
|
||||||
|
s.complete = true
|
||||||
|
if onComplete := s.onComplete; onComplete != nil {
|
||||||
|
onComplete()
|
||||||
|
}
|
||||||
|
} else if s.keysDown[key] && s.keysDown.CountPressed(s.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)
|
s.TryPlayerMove(dir, key)
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -1,8 +1,8 @@
|
|||||||
sprite:
|
sprite:
|
||||||
texture: main_character
|
texture: dragon
|
||||||
|
|
||||||
part:
|
part:
|
||||||
name: main_character
|
name: dragon
|
||||||
sub_texture: 0,0,200,400
|
sub_texture: 0,0,200,400
|
||||||
anchor: 100,350
|
anchor: 100,350
|
||||||
scale: 2
|
scale: 2
|
11
cmd/krampus19/res/sprites/villain.txt
Normal file
11
cmd/krampus19/res/sprites/villain.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
sprite:
|
||||||
|
texture: villain
|
||||||
|
|
||||||
|
part:
|
||||||
|
name: villain
|
||||||
|
sub_texture: 0,0,200,400
|
||||||
|
anchor: 100,350
|
||||||
|
scale: 2
|
||||||
|
:part
|
||||||
|
|
||||||
|
:sprite
|
Loading…
Reference in New Issue
Block a user