2021-08-06 22:00:18 +00:00
|
|
|
package tins2021
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math/rand"
|
|
|
|
|
|
|
|
"opslag.de/schobers/geom"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Level struct {
|
2021-08-08 23:25:51 +00:00
|
|
|
Player geom.Point
|
|
|
|
Lives int
|
2021-08-09 07:25:06 +00:00
|
|
|
Stars int
|
2021-08-08 23:25:51 +00:00
|
|
|
StarsCollected int
|
|
|
|
Tiles Tiles
|
|
|
|
Monsters Monsters
|
|
|
|
MonsterTargets map[geom.Point]geom.Point
|
|
|
|
Bounds geom.Rectangle
|
2021-08-06 22:00:18 +00:00
|
|
|
}
|
|
|
|
|
2021-08-08 23:25:51 +00:00
|
|
|
func NewLevel() *Level {
|
|
|
|
const dims = 12
|
|
|
|
f := &Level{
|
|
|
|
Player: geom.Pt(1, 1),
|
|
|
|
Lives: 3,
|
2021-08-09 07:25:06 +00:00
|
|
|
Stars: 0,
|
2021-08-08 23:25:51 +00:00
|
|
|
StarsCollected: 0,
|
|
|
|
Tiles: Tiles{},
|
|
|
|
Monsters: Monsters{},
|
|
|
|
MonsterTargets: map[geom.Point]geom.Point{},
|
|
|
|
Bounds: geom.Rect(1, 1, dims+1, dims+1),
|
2021-08-06 22:00:18 +00:00
|
|
|
}
|
2021-08-08 23:25:51 +00:00
|
|
|
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{}
|
|
|
|
}
|
2021-08-06 22:00:18 +00:00
|
|
|
}
|
2021-08-08 23:25:51 +00:00
|
|
|
return f
|
2021-08-06 22:00:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l Level) CanPlayerMove(dir Direction) (geom.Point, bool) {
|
2021-08-08 23:25:51 +00:00
|
|
|
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
|
2021-08-06 22:00:18 +00:00
|
|
|
}
|
|
|
|
}
|
2021-08-08 23:25:51 +00:00
|
|
|
return true
|
2021-08-06 22:00:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Level) MovePlayer(dir Direction) bool {
|
|
|
|
towards, allowed := l.CanPlayerMove(dir)
|
|
|
|
if !allowed {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
l.Player = towards
|
2021-08-08 23:25:51 +00:00
|
|
|
tile := l.Tiles[towards]
|
|
|
|
if tile.Heart {
|
|
|
|
l.Lives++
|
|
|
|
tile.Heart = false
|
|
|
|
}
|
|
|
|
if tile.Star {
|
|
|
|
l.StarsCollected++
|
|
|
|
tile.Star = false
|
|
|
|
}
|
2021-08-06 22:00:18 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-08-08 23:25:51 +00:00
|
|
|
func (l *Level) Randomize(difficulty int, stars int) {
|
|
|
|
if difficulty < 0 {
|
|
|
|
difficulty = 0
|
2021-08-06 22:00:18 +00:00
|
|
|
}
|
2021-08-08 23:25:51 +00:00
|
|
|
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()
|
2021-08-06 22:00:18 +00:00
|
|
|
}
|
2021-08-08 23:25:51 +00:00
|
|
|
}
|
2021-08-09 07:25:06 +00:00
|
|
|
l.Stars = stars
|
2021-08-08 23:25:51 +00:00
|
|
|
for stars > 0 {
|
|
|
|
i := rand.Intn(len(positions))
|
|
|
|
pos := positions[i]
|
|
|
|
if l.Tiles[pos].Occupied() {
|
|
|
|
continue
|
2021-08-06 22:00:18 +00:00
|
|
|
}
|
2021-08-08 23:25:51 +00:00
|
|
|
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--
|
2021-08-06 22:00:18 +00:00
|
|
|
}
|
|
|
|
}
|