carotidartillery/creep.go

355 lines
6.2 KiB
Go
Raw Normal View History

2021-10-06 04:05:02 +00:00
package main
import (
2021-10-27 02:21:26 +00:00
"log"
2021-10-08 04:00:33 +00:00
"math"
2021-10-06 04:05:02 +00:00
"math/rand"
"sync"
2021-10-11 06:10:58 +00:00
"time"
2021-10-06 04:05:02 +00:00
"github.com/hajimehoshi/ebiten/v2"
)
2021-10-11 06:10:58 +00:00
const (
TypeVampire = iota
TypeBat
TypeGhost
2021-10-29 15:49:27 +00:00
TypeSoul
2021-10-27 02:21:26 +00:00
TypeTorch
2021-10-11 06:10:58 +00:00
)
2021-10-06 14:18:38 +00:00
type gameCreep struct {
2021-10-11 06:10:58 +00:00
x, y float64
sprites []*ebiten.Image
frame int
frames int
lastFrame time.Time
creepType int
2021-10-06 04:05:02 +00:00
moveX, moveY float64
tick int
nextAction int
2021-10-08 04:00:33 +00:00
level *Level
player *gamePlayer
2021-10-06 14:18:38 +00:00
2021-10-06 15:03:13 +00:00
health int
2021-10-27 02:21:26 +00:00
angle float64
2021-10-06 04:05:02 +00:00
sync.Mutex
}
2021-10-27 02:21:26 +00:00
func newCreep(creepType int, l *Level, p *gamePlayer) *gameCreep {
sprites := []*ebiten.Image{
imageAtlas[ImageVampire1],
imageAtlas[ImageVampire2],
imageAtlas[ImageVampire3],
imageAtlas[ImageVampire2],
}
2021-10-28 03:52:02 +00:00
startingHealth := 1
2021-10-27 02:21:26 +00:00
if creepType == TypeBat {
sprites = []*ebiten.Image{
batSS.Frame1,
batSS.Frame2,
batSS.Frame3,
batSS.Frame4,
batSS.Frame5,
batSS.Frame6,
batSS.Frame7,
}
2021-10-28 03:52:02 +00:00
startingHealth = 2
2021-10-27 02:21:26 +00:00
} else if creepType == TypeGhost {
sprites = []*ebiten.Image{
imageAtlas[ImageGhost1],
}
2021-10-28 03:52:02 +00:00
startingHealth = 1
2021-10-29 15:49:27 +00:00
} else if creepType == TypeSoul {
sprites = []*ebiten.Image{
ojasDungeonSS.Soul1,
}
2021-10-27 02:21:26 +00:00
} else if creepType == TypeTorch {
sprites = []*ebiten.Image{
sandstoneSS.TorchTop1,
sandstoneSS.TorchTop2,
sandstoneSS.TorchTop3,
sandstoneSS.TorchTop4,
sandstoneSS.TorchTop5,
sandstoneSS.TorchTop6,
sandstoneSS.TorchTop7,
sandstoneSS.TorchTop8,
}
}
startingFrame := 0
if len(sprites) > 1 {
startingFrame = rand.Intn(len(sprites))
}
2021-10-28 03:52:02 +00:00
var x, y float64
if creepType != TypeTorch {
x, y = l.newSpawnLocation()
}
2021-10-27 02:21:26 +00:00
return &gameCreep{
creepType: creepType,
x: x,
y: y,
sprites: sprites,
frames: len(sprites),
frame: startingFrame,
level: l,
player: p,
2021-10-28 03:52:02 +00:00
health: startingHealth,
2021-10-27 02:21:26 +00:00
}
}
2021-10-11 06:10:58 +00:00
func (c *gameCreep) queueNextAction() {
if c.creepType == TypeBat {
c.nextAction = 288 + rand.Intn(288)
return
}
2021-10-20 01:46:11 +00:00
c.nextAction = 288 + rand.Intn(432)
2021-10-11 06:10:58 +00:00
}
2021-10-12 03:22:13 +00:00
func (c *gameCreep) runAway() {
c.queueNextAction()
2021-10-29 15:49:27 +00:00
randMovementA := ((rand.Float64() - 0.5) * c.moveSpeed()) / 8
randMovementB := ((rand.Float64() - 0.5) * c.moveSpeed()) / 8
2021-10-12 03:22:13 +00:00
c.moveX = c.x - c.player.x
if c.moveX < 0 {
c.moveX = math.Abs(randMovementA) * -1
} else {
c.moveX = math.Abs(randMovementA)
}
c.moveY = c.y - c.player.y
if c.moveY < 0 {
c.moveY = math.Abs(randMovementB) * -1
} else {
c.moveY = math.Abs(randMovementB)
}
}
2021-10-29 15:49:27 +00:00
func (c *gameCreep) moveSpeed() float64 {
if c.creepType == TypeSoul {
return 0.5 / 4
}
return 0.4
}
2021-10-20 01:46:11 +00:00
func (c *gameCreep) seekPlayer() {
2021-10-29 15:49:27 +00:00
maxSpeed := c.moveSpeed() / 9
minSpeed := c.moveSpeed() / 5 / 9
if c.creepType == TypeSoul {
maxSpeed *= 5
}
2021-10-11 07:52:48 +00:00
2021-10-20 01:46:11 +00:00
a := angle(c.x, c.y, c.player.x, c.player.y)
c.moveX = -math.Cos(a)
c.moveY = -math.Sin(a)
for {
if (c.moveX < -minSpeed || c.moveX > minSpeed) || (c.moveY < -minSpeed || c.moveY > minSpeed) {
break
2021-10-08 04:00:33 +00:00
}
2021-10-11 07:52:48 +00:00
2021-10-20 01:46:11 +00:00
c.moveX *= 1.1
c.moveY *= 1.1
}
for {
if c.moveX >= -maxSpeed && c.moveX <= maxSpeed && c.moveY >= -maxSpeed && c.moveY <= maxSpeed {
break
2021-10-12 04:53:55 +00:00
}
2021-10-20 01:46:11 +00:00
c.moveX *= 0.9
c.moveY *= 0.9
}
2021-10-29 15:49:27 +00:00
c.tick = 0
2021-10-20 01:46:11 +00:00
c.nextAction = 1440
}
func (c *gameCreep) doNextAction() {
c.queueNextAction()
2021-10-29 15:49:27 +00:00
randMovementA := ((rand.Float64() - 0.5) * c.moveSpeed()) / 12
randMovementB := ((rand.Float64() - 0.5) * c.moveSpeed()) / 12
2021-10-20 01:46:11 +00:00
2021-10-27 02:21:26 +00:00
if c.creepType == TypeGhost {
c.angle = angle(c.x, c.y, c.player.x, c.player.y)
// TODO optimize
if c.angle > math.Pi/2 || c.angle < -1*math.Pi/2 {
c.sprites = []*ebiten.Image{
imageAtlas[ImageGhost1R],
}
c.angle = c.angle - math.Pi
} else {
c.sprites = []*ebiten.Image{
imageAtlas[ImageGhost1],
}
}
}
2021-10-20 01:46:11 +00:00
repelled := c.repelled()
2021-10-29 15:49:27 +00:00
if !repelled && rand.Intn(13) == 0 && c.creepType != TypeSoul {
2021-10-20 01:46:11 +00:00
c.seekPlayer()
2021-10-08 04:00:33 +00:00
} else {
c.moveX = randMovementA
c.moveY = randMovementB
}
2021-10-06 15:03:13 +00:00
if c.x <= 2 && c.moveX < 0 {
c.moveX *= 1
} else if c.x >= float64(c.level.w-3) && c.moveX > 0 {
c.moveX *= 1
}
if c.y <= 2 && c.moveY > 0 {
c.moveY *= 1
} else if c.y >= float64(c.level.h-3) && c.moveY < 0 {
c.moveY *= 1
}
2021-10-06 04:05:02 +00:00
}
2021-10-20 01:46:11 +00:00
func (c *gameCreep) repelled() bool {
2021-10-29 15:49:27 +00:00
if c.creepType == TypeSoul {
return false
}
2021-10-22 01:07:59 +00:00
repelled := !c.player.garlicUntil.IsZero() || !c.player.holyWaterUntil.IsZero()
2021-10-20 01:46:11 +00:00
return repelled
}
2021-10-27 02:21:26 +00:00
// TODO return true when creep is facing player and player is facing creep
func (c *gameCreep) facingPlayer() bool {
mod := func(v float64) float64 {
for v > math.Pi {
v -= math.Pi
}
for v < math.Pi*-1 {
v += math.Pi
}
return v
}
_ = mod
ca := math.Remainder(c.angle, math.Pi)
ca = math.Remainder(ca, -math.Pi)
if ca < 0 {
ca = math.Pi + ca
}
ca = c.angle
if ca < 0 {
ca = math.Pi*2 + ca
}
pa := math.Remainder(c.player.angle, math.Pi)
pa = math.Remainder(pa, -math.Pi)
if pa < 0 {
pa = math.Pi + pa
}
pa = c.player.angle
if pa < 0 {
pa = math.Pi*2 + pa
}
a := ca - pa
if pa > ca {
a = pa - ca
}
if rand.Intn(70) == 0 {
// TODO
log.Println(ca, pa, a)
}
return a < 2
//a2 := c.player.angle - c.angle
// TODO
//return a > math.Pi/2*-1 && a2 > math.Pi/2*-1 && a < math.Pi/2 && a2 < math.Pi/2
}
2021-10-06 14:18:38 +00:00
func (c *gameCreep) Update() {
2021-10-06 04:05:02 +00:00
c.Lock()
defer c.Unlock()
2021-10-06 15:03:13 +00:00
if c.health == 0 {
return
}
2021-10-27 02:21:26 +00:00
if c.creepType == TypeTorch {
return
}
2021-10-06 04:05:02 +00:00
c.tick++
2021-10-20 01:46:11 +00:00
repelled := c.repelled()
2021-10-27 02:21:26 +00:00
if c.creepType == TypeGhost && c.facingPlayer() {
return
}
2021-10-20 01:46:11 +00:00
dx, dy := deltaXY(c.x, c.y, c.player.x, c.player.y)
2021-10-29 15:49:27 +00:00
seekDistance := 3.5
2021-10-20 01:46:11 +00:00
if !repelled && dx < seekDistance && dy < seekDistance {
c.queueNextAction()
c.seekPlayer()
} else if c.tick >= c.nextAction {
2021-10-06 04:05:02 +00:00
c.doNextAction()
c.tick = 0
}
2021-10-06 15:03:13 +00:00
x, y := c.x+c.moveX, c.y+c.moveY
2021-10-27 03:11:07 +00:00
if c.level.isFloor(x, y) {
c.x, c.y = x, y
2021-10-27 03:11:07 +00:00
} else if c.level.isFloor(x, c.y) {
c.x = x
2021-10-27 03:11:07 +00:00
} else if c.level.isFloor(c.x, y) {
c.y = y
} else {
2021-10-06 15:03:13 +00:00
c.nextAction = 0
2021-10-12 03:22:13 +00:00
return
}
2021-10-20 01:46:11 +00:00
if repelled {
2021-10-12 03:22:13 +00:00
dx, dy := deltaXY(c.x, c.y, c.player.x, c.player.y)
if dx <= 3 && dy <= 3 {
c.runAway()
}
}
for _, item := range c.level.items {
if item.health == 0 {
continue
}
dx, dy := deltaXY(c.x, c.y, item.x, item.y)
if dx <= 2 && dy <= 2 {
c.runAway()
}
2021-10-06 15:03:13 +00:00
}
2021-10-06 04:05:02 +00:00
}
2021-10-06 14:18:38 +00:00
func (c *gameCreep) Position() (float64, float64) {
2021-10-06 04:05:02 +00:00
c.Lock()
defer c.Unlock()
return c.x, c.y
}
2021-10-11 07:52:48 +00:00
func (c *gameCreep) killScore() int {
switch c.creepType {
case TypeVampire:
return 50
case TypeBat:
return 125
case TypeGhost:
2021-10-27 02:21:26 +00:00
return 150
2021-10-11 07:52:48 +00:00
default:
return 0
}
}