tins2021/level.go
Sander Schobers 13b6a50a45 Added bitmap font.
Added monsters.
Added animations.
Added rendering of geometries.
2021-08-09 01:25:51 +02:00

166 lines
3.3 KiB
Go

package tins2021
import (
"math/rand"
"opslag.de/schobers/geom"
)
type Level struct {
Player geom.Point
Lives int
StarsCollected int
Tiles Tiles
Monsters Monsters
MonsterTargets map[geom.Point]geom.Point
Bounds geom.Rectangle
}
func NewLevel() *Level {
const dims = 12
f := &Level{
Player: geom.Pt(1, 1),
Lives: 3,
StarsCollected: 0,
Tiles: Tiles{},
Monsters: Monsters{},
MonsterTargets: map[geom.Point]geom.Point{},
Bounds: geom.Rect(1, 1, dims+1, dims+1),
}
for y := 1; y <= dims; y++ {
endX := dims
if y%2 == 0 {
endX--
}
for x := 1; x <= endX; x++ {
f.Tiles[geom.Pt(x, y)] = &Tile{}
}
}
return f
}
func (l Level) CanPlayerMove(dir Direction) (geom.Point, bool) {
return l.Tiles.CanMove(l.Player, dir)
}
func (l Level) CanMonsterMove(p geom.Point, dir Direction) (geom.Point, bool) {
q, ok := l.Tiles.CanMove(p, dir)
if !ok {
return geom.Point{}, false
}
if l.CanMonsterMoveTo(q) {
return q, true
}
return geom.Point{}, false
}
func (l Level) CanMonsterMoveTo(p geom.Point) bool {
if l.Tiles[p].Occupied() {
return false
}
if _, ok := l.Monsters[p]; ok {
return false
}
for _, target := range l.MonsterTargets {
if p == target {
return false
}
}
return true
}
func (l *Level) MovePlayer(dir Direction) bool {
towards, allowed := l.CanPlayerMove(dir)
if !allowed {
return false
}
l.Player = towards
tile := l.Tiles[towards]
if tile.Heart {
l.Lives++
tile.Heart = false
}
if tile.Star {
l.StarsCollected++
tile.Star = false
}
return true
}
func (l *Level) Randomize(difficulty int, stars int) {
if difficulty < 0 {
difficulty = 0
}
positions := make([]geom.Point, 0, len(l.Tiles))
for pos := range l.Tiles {
positions = append(positions, pos)
}
flip := difficulty * len(l.Tiles) / 200
if flip > len(l.Tiles)/2 {
flip = len(l.Tiles) / 2
}
for ; flip > 0; flip-- {
for {
i := rand.Intn(len(positions))
pos := positions[i]
if l.Tiles[pos].Inversed {
continue
}
l.Tiles[pos].Invert()
if l.Tiles.AllReachable(l.Player) {
break
}
l.Tiles[pos].Invert()
}
}
for stars > 0 {
i := rand.Intn(len(positions))
pos := positions[i]
if l.Tiles[pos].Occupied() {
continue
}
l.Tiles[pos].Star = true
stars--
}
hearts := 1 + (80-difficulty)*4/80 // [5..0]
for hearts > 0 {
i := rand.Intn(len(positions))
pos := positions[i]
if l.Tiles[pos].Occupied() {
continue
}
l.Tiles[pos].Heart = true
hearts--
}
monsters := 1 + (4 * difficulty / 100)
minRandomMonster := (100 - difficulty)
minChaserMonster := (200 - difficulty) / 2
for monsters > 0 {
i := rand.Intn(len(positions))
pos := positions[i]
curr := l.Monsters[pos]
if l.Tiles[pos].Occupied() || curr != nil {
continue
}
monster := MonsterTypeStraight
m := rand.Intn(100)
if m >= minChaserMonster {
monster = MonsterTypeChaser
} else if m >= minRandomMonster {
monster = MonsterTypeRandom
}
switch monster {
case MonsterTypeStraight:
l.Monsters[pos] = &StraightWalkingMonster{Direction: RandomDirection()}
case MonsterTypeRandom:
l.Monsters[pos] = &RandomWalkingMonster{}
case MonsterTypeChaser:
l.Monsters[pos] = &ChasingMonster{}
default:
panic("not implemented")
// l.Monsters[pos] = monster
}
monsters--
}
}