210 lines
5.1 KiB
Go
210 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
"time"
|
|
|
|
"opslag.de/schobers/allg5"
|
|
"opslag.de/schobers/geom"
|
|
"opslag.de/schobers/krampus19/gut"
|
|
)
|
|
|
|
type playLevelState struct {
|
|
ctx *Context
|
|
|
|
pack levelPack
|
|
level level
|
|
player *entity
|
|
villain *entity
|
|
bricks entityList
|
|
sunken entityList
|
|
splash map[geom.Point]*splashAnimation
|
|
|
|
steps int
|
|
complete bool
|
|
onComplete func()
|
|
|
|
tick time.Duration
|
|
ani gut.Animations
|
|
keysDown keyPressedState
|
|
}
|
|
|
|
func (s *playLevelState) Entities() entityList {
|
|
var entities entityList
|
|
return entities.Add(s.player).Add(s.villain).AddList(s.bricks)
|
|
}
|
|
|
|
func (s *playLevelState) Particles(at geom.Point) []particle {
|
|
var particles []particle
|
|
for pos, ani := range s.splash {
|
|
if pos != at {
|
|
continue
|
|
}
|
|
log.Println("Found particles at", at)
|
|
for _, p := range ani.particles {
|
|
particles = append(particles, p.part)
|
|
}
|
|
}
|
|
return particles
|
|
}
|
|
|
|
func (s *playLevelState) FindSunkenBrick(pos geom.Point) *entity {
|
|
return s.sunken.FindEntity(pos)
|
|
}
|
|
|
|
func (s *playLevelState) IsNextToMagma(pos geom.Point) bool {
|
|
return s.checkTile(pos.Add2D(1, 0), s.isMagma) ||
|
|
s.checkTile(pos.Add2D(-1, 0), s.isMagma) ||
|
|
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, onComplete func()) {
|
|
s.ctx = ctx
|
|
s.pack = ctx.Levels[pack]
|
|
s.level = s.pack.levels[level]
|
|
s.bricks = nil
|
|
s.sunken = nil
|
|
s.splash = map[geom.Point]*splashAnimation{}
|
|
for i, e := range s.level.entities {
|
|
switch e {
|
|
case entityTypeBrick:
|
|
s.bricks = append(s.bricks, newEntity(e, s.level.idxToPos(i)))
|
|
case entityTypeCharacter:
|
|
s.player = newEntity(e, s.level.idxToPos(i))
|
|
case entityTypeVillain:
|
|
s.villain = newEntity(e, s.level.idxToPos(i))
|
|
}
|
|
}
|
|
s.keysDown = keyPressedState{}
|
|
s.onComplete = onComplete
|
|
}
|
|
|
|
func (s *playLevelState) Level() level { return s.level }
|
|
|
|
func (s *playLevelState) PressKey(key allg5.Key) {
|
|
s.keysDown[key] = true
|
|
}
|
|
|
|
func (s *playLevelState) ReleaseKey(key allg5.Key) {
|
|
s.keysDown[key] = false
|
|
}
|
|
|
|
func (s *playLevelState) Steps() int { return s.steps }
|
|
|
|
func (s *playLevelState) Tick(now time.Duration) {
|
|
s.ani.Animate(now)
|
|
}
|
|
|
|
func (s *playLevelState) TryPlayerMove(dir geom.Point, key allg5.Key) {
|
|
if s.player.scr.pos != s.player.pos.ToF32() {
|
|
return
|
|
}
|
|
|
|
to := s.player.pos.Add(dir)
|
|
if !s.canMove(s.player.pos, dir) {
|
|
log.Printf("Move is not allowed (tried out move to %s after key '%s' was pressed)", to, gut.KeyToString(key))
|
|
return
|
|
}
|
|
|
|
s.steps++
|
|
log.Printf("Moving player to %s", to)
|
|
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
|
|
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)
|
|
}
|
|
})
|
|
|
|
if brick := s.bricks.FindEntity(to); brick != nil {
|
|
log.Printf("Pushing brick at %s", to)
|
|
brickTo := to.Add(dir)
|
|
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)
|
|
s.bricks = s.bricks.Remove(brickTo)
|
|
s.sunken = s.sunken.Add(brick)
|
|
s.ani.Start(s.ctx.Tick, newSinkAnimation(brick))
|
|
|
|
splash := newSplashAnimation(brickTo)
|
|
s.splash[brickTo] = splash
|
|
s.ani.StartFn(s.ctx.Tick, splash, func() {
|
|
delete(s.splash, brickTo)
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (s *playLevelState) canMove(from, dir geom.Point) bool {
|
|
to := from.Add(dir)
|
|
if !s.checkTile(to, s.isSolidTile) {
|
|
return false
|
|
}
|
|
brick := s.bricks.FindEntity(to)
|
|
if brick != nil {
|
|
brickTo := to.Add(dir)
|
|
return !s.checkTileNotFound(brickTo, s.isObstructed, true)
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (s *playLevelState) checkTile(pos geom.Point, check func(pos geom.Point, idx int, t tile) bool) bool {
|
|
return s.checkTileNotFound(pos, check, false)
|
|
}
|
|
|
|
func (s *playLevelState) checkTileNotFound(pos geom.Point, check func(pos geom.Point, idx int, t tile) bool, notFound bool) bool {
|
|
idx := s.level.posToIdx(pos)
|
|
if idx == -1 {
|
|
return notFound
|
|
}
|
|
return check(pos, idx, s.level.tiles[idx])
|
|
}
|
|
|
|
func (s *playLevelState) findEntityAt(pos geom.Point) *entity {
|
|
if s.player.pos == pos {
|
|
return s.player
|
|
}
|
|
brick := s.bricks.FindEntity(pos)
|
|
if brick != nil {
|
|
return brick
|
|
}
|
|
return s.sunken.FindEntity(pos)
|
|
}
|
|
|
|
func (s *playLevelState) isObstructed(pos geom.Point, idx int, t tile) bool {
|
|
if s.bricks.FindEntity(pos) != nil {
|
|
return true // brick
|
|
}
|
|
switch s.level.tiles[idx] {
|
|
case tileMagma:
|
|
return false
|
|
case tileBasic:
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (s *playLevelState) isMagma(pos geom.Point, idx int, t tile) bool { return t == tileMagma }
|
|
|
|
func (s *playLevelState) isSolidTile(pos geom.Point, idx int, t tile) bool {
|
|
switch t {
|
|
case tileBasic:
|
|
return true
|
|
case tileMagma:
|
|
return s.sunken.FindEntity(pos) != nil
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *playLevelState) wouldBrickSink(pos geom.Point, idx int, t tile) bool {
|
|
return t == tileMagma && s.sunken.FindEntity(pos) == nil
|
|
}
|