Added win condition.

Renamed entities on disk & added missing sprites.
Moved entity and animations to separate code unit.
This commit is contained in:
Sander Schobers 2019-12-29 10:01:34 +01:00
parent aa47e2cabb
commit 356b510286
12 changed files with 146 additions and 80 deletions

View File

@ -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]
case 2:
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:
m.Top, m.Left, m.Bottom, m.Right = margins[0], margins[1], margins[2], margins[3]
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
}

View File

@ -182,7 +182,7 @@ func newSettingsHeader(label string) alui.Control {
header := &settingsHeader{}
header.Font = "header"
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 {

42
cmd/krampus19/entity.go Normal file
View 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
}

View File

@ -150,7 +150,8 @@ func (g *game) loadAssets() error {
log.Println("Loading textures...")
err := g.loadTextures(map[string]string{
"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_magma.png": "magma",
@ -177,7 +178,7 @@ func (g *game) loadAssets() error {
log.Printf("Loaded %d fonts.\n", g.ui.Fonts().Len())
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 {
return err
}

View File

@ -12,6 +12,23 @@ type levelPack struct {
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 {
name string
levels []string

View File

@ -21,7 +21,6 @@ func (s *levelSelect) Enter(ctx *Context) error {
s.Init()
name := func(id string) string { return fmt.Sprintf("Level %s", id) }
for _, id := range s.pack.order {
// level := s.pack[id]
levelID := id
s.Add(name(levelID), func() {
s.ctx.Navigation.PlayLevel(s.packID, levelID)

View File

@ -3,7 +3,6 @@ package main
import (
"fmt"
"sort"
"time"
"opslag.de/schobers/allg5"
"opslag.de/schobers/geom"
@ -17,6 +16,7 @@ type playLevel struct {
init bool
menu *alui.Menu
end alui.Control
showMenu bool
packID string
@ -39,43 +39,6 @@ func (s keyPressedState) CountPressed(keys ...allg5.Key) int {
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 {
l.ctx = ctx
@ -88,12 +51,27 @@ func (l *playLevel) Enter(ctx *Context) error {
l.menu.OnEscape = func() { l.showMenu = false }
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
}
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 {
pos := l.posToCabinet(p.Add2D(.5, .5)).Add2D(0, z)
return pos.Mul(l.scale).Add(l.offset)
@ -156,11 +134,12 @@ func (l *playLevel) Handle(e allg5.Event) {
l.state.ReleaseKey(e.KeyCode)
}
if l.showMenu {
switch {
case l.showMenu:
l.menu.Handle(e)
return
}
case l.state.complete:
l.end.Handle(e)
default:
switch e := e.(type) {
case *allg5.KeyCharEvent:
switch e.KeyCode {
@ -178,6 +157,7 @@ func (l *playLevel) Handle(e allg5.Event) {
}
}
}
}
func (l *playLevel) drawSprite(name, partName string, pos geom.PointF32) {
l.drawSpritePart(name, partName, pos, 0)
@ -217,10 +197,12 @@ func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) {
for _, e := range entities {
switch e.typ {
case entityTypeCharacter:
l.drawSprite("main_character", "main_character", e.scr)
case entityTypeBrick:
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())
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))
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)
}
}

View File

@ -35,7 +35,11 @@ type playLevelState struct {
villain *entity
bricks []*entity
sunken []*entity
steps int
complete bool
onComplete func()
tick time.Duration
ani gut.Animations
keysDown keyPressedState
@ -44,9 +48,7 @@ type playLevelState struct {
func (s *playLevelState) Entities() []*entity {
var entities []*entity
entities = append(entities, s.player)
if s.villain != nil {
entities = append(entities, s.villain)
}
entities = append(entities, s.bricks...)
return entities
}
@ -62,7 +64,7 @@ func (s *playLevelState) IsNextToMagma(pos geom.Point) bool {
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.pack = ctx.Levels[pack]
s.level = s.pack.levels[level]
@ -79,6 +81,7 @@ func (s *playLevelState) Init(ctx *Context, pack, level string) {
}
}
s.keysDown = keyPressedState{}
s.onComplete = onComplete
}
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)
s.ani.StartFn(s.ctx.Tick, newEntityMoveAnimation(s.player, to), func() {
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))
s.TryPlayerMove(dir, key)
}

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,8 +1,8 @@
sprite:
texture: main_character
texture: dragon
part:
name: main_character
name: dragon
sub_texture: 0,0,200,400
anchor: 100,350
scale: 2

View File

@ -0,0 +1,11 @@
sprite:
texture: villain
part:
name: villain
sub_texture: 0,0,200,400
anchor: 100,350
scale: 2
:part
:sprite