diff --git a/cmd/krampus19/entity.go b/cmd/krampus19/entity.go index e13eda0..dc9d0eb 100644 --- a/cmd/krampus19/entity.go +++ b/cmd/krampus19/entity.go @@ -9,34 +9,59 @@ import ( type entity struct { typ entityType pos geom.Point - scr geom.PointF32 + scr entityLoc +} + +type entityLoc struct { + pos geom.PointF32 + z float32 } func newEntity(typ entityType, pos geom.Point) *entity { - return &entity{typ, pos, pos.ToF32()} + return &entity{typ, pos, entityLoc{pos.ToF32(), 0}} } -type entityMoveAnimation struct { +type moveAnimation 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()} +func newMoveAnimation(e *entity, to geom.Point) *moveAnimation { + ani := &moveAnimation{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 { +func (a *moveAnimation) 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 + a.e.scr.pos = to return false } - a.e.scr = to.Sub(from).Mul(progress).Add(from) + a.e.scr.pos = to.Sub(from).Mul(progress).Add(from) + return true +} + +type sinkAnimation struct { + e *entity +} + +func newSinkAnimation(e *entity) *sinkAnimation { + return &sinkAnimation{e: e} +} + +func (a *sinkAnimation) Animate(start, now time.Duration) bool { + const duration = 70 * time.Millisecond + + progress := float32((now-start)*1000/duration) * .001 + if progress >= 1 { + a.e.scr.z = 80 + return false + } + a.e.scr.z = progress * 80 return true } diff --git a/cmd/krampus19/playlevel.go b/cmd/krampus19/playlevel.go index 9495072..30854f4 100644 --- a/cmd/krampus19/playlevel.go +++ b/cmd/krampus19/playlevel.go @@ -174,40 +174,38 @@ func (l *playLevel) Handle(e allg5.Event) { } } -func (l *playLevel) drawSprite(name, partName string, pos geom.PointF32) { - l.drawSpritePart(name, partName, pos, 0) -} - -func (l *playLevel) drawSpritePart(name, partName string, pos geom.PointF32, z float32) { - l.ctx.SpriteDrawer.Draw(name, partName, l.posToScreenF32(pos, z), DrawSpriteOptions{Scale: l.scale}) +func (l *playLevel) drawSprite(name, partName string, pos entityLoc) { + l.ctx.SpriteDrawer.Draw(name, partName, l.posToScreenF32(pos.pos, pos.z), DrawSpriteOptions{Scale: l.scale}) } func (l *playLevel) Render(ctx *alui.Context, bounds geom.RectangleF32) { level := l.state.level for i, t := range level.tiles { pos := geom.Pt(i%level.width, i/level.width) + scr := entityLoc{pos.ToF32(), 0} switch t { case tileBasic: if l.state.IsNextToMagma(pos) { - l.drawSprite("lava_brick", "magma", pos.ToF32()) + l.drawSprite("lava_brick", "magma", scr) } else { - l.drawSprite("lava_brick", "lava_brick", pos.ToF32()) + l.drawSprite("lava_brick", "lava_brick", scr) } case tileMagma: - l.drawSprite("magma", "magma", pos.ToF32()) - if l.state.IsFilledUp(pos) { - l.drawSpritePart("brick", "brick", pos.ToF32(), 80) - l.drawSprite("magma", "sunken_overlay", pos.ToF32()) + l.drawSprite("magma", "magma", scr) + brick := l.state.FindSunkenBrick(pos) + if brick != nil { + l.drawSprite("brick", "brick", brick.scr) + l.drawSprite("magma", "sunken_overlay", scr) } } } entities := l.state.Entities() sort.Slice(entities, func(i, j int) bool { - if entities[i].scr.Y == entities[j].scr.Y { - return entities[i].scr.X < entities[j].scr.X + if entities[i].scr.pos.Y == entities[j].scr.pos.Y { + return entities[i].scr.pos.X < entities[j].scr.pos.X } - return entities[i].scr.Y < entities[j].scr.Y + return entities[i].scr.pos.Y < entities[j].scr.pos.Y }) for _, e := range entities { diff --git a/cmd/krampus19/playlevelstate.go b/cmd/krampus19/playlevelstate.go index 6db5166..344504f 100644 --- a/cmd/krampus19/playlevelstate.go +++ b/cmd/krampus19/playlevelstate.go @@ -53,8 +53,8 @@ func (s *playLevelState) Entities() []*entity { return entities } -func (s *playLevelState) IsFilledUp(pos geom.Point) bool { - return findEntityAt(s.sunken, pos) != nil +func (s *playLevelState) FindSunkenBrick(pos geom.Point) *entity { + return findEntityAt(s.sunken, pos) } func (s *playLevelState) IsNextToMagma(pos geom.Point) bool { @@ -113,7 +113,7 @@ func (s *playLevelState) TryPlayerMove(dir geom.Point, key allg5.Key) { s.steps++ log.Printf("Moving player to %s", to) - s.ani.StartFn(s.ctx.Tick, newEntityMoveAnimation(s.player, to), func() { + s.ani.StartFn(s.ctx.Tick, newMoveAnimation(s.player, to), func() { log.Printf("Player movement finished") if s.player.pos == s.villain.pos { s.complete = true @@ -129,13 +129,14 @@ func (s *playLevelState) TryPlayerMove(dir geom.Point, key allg5.Key) { if brick := findEntityAt(s.bricks, to); brick != nil { log.Printf("Pushing brick at %s", to) brickTo := to.Add(dir) - s.ani.StartFn(s.ctx.Tick, newEntityMoveAnimation(brick, brickTo), func() { + s.ani.StartFn(s.ctx.Tick, newMoveAnimation(brick, brickTo), func() { log.Printf("Brick movement finished") if s.checkTile(brickTo, s.wouldBrickSink) { log.Printf("Sinking brick at %s", brickTo) idx := findEntityIdx(s.bricks, brickTo) s.bricks = append(s.bricks[:idx], s.bricks[idx+1:]...) s.sunken = append(s.sunken, brick) + s.ani.Start(s.ctx.Tick, newSinkAnimation(brick)) } }) }