Add basic animal AI
This commit is contained in:
parent
9ab05fe40b
commit
a9c8114096
|
@ -1,6 +1,6 @@
|
||||||
import * as planck from 'planck'
|
import * as planck from 'planck'
|
||||||
import { AnimalDefinition, CreatureDefinition } from '../data/animals'
|
import { AnimalDefinition, CreatureDefinition } from '../data/animals'
|
||||||
import { Action } from "./Action"
|
import { Action, adjustAction } from "./Action"
|
||||||
import { Entity, Sensor } from "./Entity"
|
import { Entity, Sensor } from "./Entity"
|
||||||
import { PuddleEntity } from './PuddleEntity'
|
import { PuddleEntity } from './PuddleEntity'
|
||||||
import { WorldContext } from './World'
|
import { WorldContext } from './World'
|
||||||
|
@ -11,9 +11,13 @@ export class AnimalEntity extends Entity {
|
||||||
isPlayer: boolean = false
|
isPlayer: boolean = false
|
||||||
isMonster: boolean = false
|
isMonster: boolean = false
|
||||||
thinkTimer: number = 0 // Time since last thought, used by AI.
|
thinkTimer: number = 0 // Time since last thought, used by AI.
|
||||||
target?: Entity
|
wanderTimer: number = 0 // Time since last wander.
|
||||||
|
nextWander: number = 100 // Time to next wander.
|
||||||
|
smelledTarget?: Entity
|
||||||
|
seenTarget?: Entity
|
||||||
def: AnimalDefinition
|
def: AnimalDefinition
|
||||||
mode: CreatureDefinition
|
mode: CreatureDefinition
|
||||||
|
desiredActions: Action[] = []
|
||||||
|
|
||||||
constructor(def: AnimalDefinition) {
|
constructor(def: AnimalDefinition) {
|
||||||
super(`${def.name}.animal.stand.west.0`)
|
super(`${def.name}.animal.stand.west.0`)
|
||||||
|
@ -110,7 +114,7 @@ export class AnimalEntity extends Entity {
|
||||||
if (Math.abs(this.velocity[1]) < this.mode.maxSpeed) {
|
if (Math.abs(this.velocity[1]) < this.mode.maxSpeed) {
|
||||||
this.velocity[1] -= Math.sin(r) * this.mode.acceleration
|
this.velocity[1] -= Math.sin(r) * this.mode.acceleration
|
||||||
}
|
}
|
||||||
let cardinal = this.getCardinal()
|
let cardinal = this.getCardinal(this.direction)
|
||||||
this.sprite.setKey = 'run'
|
this.sprite.setKey = 'run'
|
||||||
if (this.sprite.subsetKey !== cardinal || this.sprite.subsetKey !== 'run') {
|
if (this.sprite.subsetKey !== cardinal || this.sprite.subsetKey !== 'run') {
|
||||||
this.sprite.setCtor(`${this.sprite.spriteKey}.${this.sprite.animationKey}.${this.sprite.setKey}.${cardinal}.${this.sprite.frameIndex}`)
|
this.sprite.setCtor(`${this.sprite.spriteKey}.${this.sprite.animationKey}.${this.sprite.setKey}.${cardinal}.${this.sprite.frameIndex}`)
|
||||||
|
@ -119,7 +123,7 @@ export class AnimalEntity extends Entity {
|
||||||
} else {
|
} else {
|
||||||
this.sprite.animate = false
|
this.sprite.animate = false
|
||||||
this.sprite.setKey = 'stand'
|
this.sprite.setKey = 'stand'
|
||||||
this.sprite.setCtor(`${this.sprite.spriteKey}.${this.sprite.animationKey}.${this.sprite.setKey}.${this.getCardinal()}.0`)
|
this.sprite.setCtor(`${this.sprite.spriteKey}.${this.sprite.animationKey}.${this.sprite.setKey}.${this.getCardinal(this.direction)}.0`)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -129,9 +133,9 @@ export class AnimalEntity extends Entity {
|
||||||
// Eh... let's manually handle velocity
|
// Eh... let's manually handle velocity
|
||||||
this.body?.setLinearVelocity(planck.Vec2(this.velocity[0], this.velocity[1]))
|
this.body?.setLinearVelocity(planck.Vec2(this.velocity[0], this.velocity[1]))
|
||||||
}
|
}
|
||||||
getCardinal(): string {
|
getCardinal(direction: number): string {
|
||||||
const degreesPerDirection = 360 / 4
|
const degreesPerDirection = 360 / 4
|
||||||
const angle = this.direction + degreesPerDirection / 2
|
const angle = direction + degreesPerDirection / 2
|
||||||
|
|
||||||
if (angle >= 0 * degreesPerDirection && angle < 1 * degreesPerDirection) {
|
if (angle >= 0 * degreesPerDirection && angle < 1 * degreesPerDirection) {
|
||||||
return 'west'
|
return 'west'
|
||||||
|
@ -144,19 +148,51 @@ export class AnimalEntity extends Entity {
|
||||||
}
|
}
|
||||||
think(delta: number) {
|
think(delta: number) {
|
||||||
this.thinkTimer += delta
|
this.thinkTimer += delta
|
||||||
while (this.thinkTimer >= 500) {
|
while (this.thinkTimer >= 100) {
|
||||||
if (!this.isMonster) {
|
if (!this.isMonster) {
|
||||||
if (this.target) {
|
if (this.seenTarget) {
|
||||||
// Flee from target
|
let r = Math.atan2(this.y-this.seenTarget.y, this.x-this.seenTarget.x)
|
||||||
|
let a = r * (180 / Math.PI)
|
||||||
|
if (a < 0) {
|
||||||
|
a += 360
|
||||||
|
}
|
||||||
|
a -= 180
|
||||||
|
if (a < 0) {
|
||||||
|
a = 360 - a
|
||||||
|
}
|
||||||
|
this.desiredActions = adjustAction([], this.getCardinal(a), 0.85)
|
||||||
|
this.desiredActions = adjustAction(this.desiredActions, this.getCardinal(Math.random()*360), Math.random())
|
||||||
|
} else if (this.smelledTarget) {
|
||||||
|
let r = Math.atan2(this.y-this.smelledTarget.y, this.x-this.smelledTarget.x)
|
||||||
|
let a = r * (180 / Math.PI)
|
||||||
|
if (a < 0) {
|
||||||
|
a += 360
|
||||||
|
}
|
||||||
|
a -= 180
|
||||||
|
if (a < 0) {
|
||||||
|
a = 360 - a
|
||||||
|
}
|
||||||
|
// 5% to walk away, otherwise just chill.
|
||||||
|
if (Math.random() > 0.95) {
|
||||||
|
this.desiredActions = adjustAction([], this.getCardinal(a), 0.85)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Wander.
|
this.wanderTimer += delta
|
||||||
|
if (this.wanderTimer >= this.nextWander) {
|
||||||
|
this.nextWander = Math.max(10, Math.random() * 100)
|
||||||
|
if (Math.random() > 0.15) {
|
||||||
|
this.desiredActions = []
|
||||||
|
} else {
|
||||||
|
this.desiredActions = adjustAction([], this.getCardinal(Math.random()*360), 1)
|
||||||
|
}
|
||||||
|
this.wanderTimer = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.thinkTimer -= 500
|
this.thinkTimer -= 100
|
||||||
|
this.act(this.desiredActions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setTarget(entity: Entity) {
|
|
||||||
}
|
|
||||||
sense(sensor: Sensor, entity: AnimalEntity) {
|
sense(sensor: Sensor, entity: AnimalEntity) {
|
||||||
if (this.isPlayer) return
|
if (this.isPlayer) return
|
||||||
if (sensor.type === 'long') {
|
if (sensor.type === 'long') {
|
||||||
|
@ -175,38 +211,22 @@ export class AnimalEntity extends Entity {
|
||||||
}
|
}
|
||||||
smell(entity: AnimalEntity) {
|
smell(entity: AnimalEntity) {
|
||||||
if (entity.isPlayer) {
|
if (entity.isPlayer) {
|
||||||
if (!this.isMonster) {
|
this.smelledTarget = entity
|
||||||
console.log('alert, we can smell player')
|
|
||||||
} else {
|
|
||||||
console.log('we smell the player, wander towards them')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsmell(entity: AnimalEntity) {
|
unsmell(entity: AnimalEntity) {
|
||||||
if (entity.isPlayer) {
|
if (entity.isPlayer) {
|
||||||
if (!this.isMonster) {
|
this.smelledTarget = undefined
|
||||||
console.log('no longer smell player, wander around')
|
|
||||||
} else {
|
|
||||||
console.log('no longer smell player, wander around')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
see(entity: AnimalEntity) {
|
see(entity: AnimalEntity) {
|
||||||
if (entity.isPlayer) {
|
if (entity.isPlayer) {
|
||||||
if (!this.isMonster) {
|
this.seenTarget = entity
|
||||||
console.log('oh no, run from player')
|
|
||||||
} else {
|
|
||||||
console.log('oh ho, chase player')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsee(entity: AnimalEntity) {
|
unsee(entity: AnimalEntity) {
|
||||||
if (entity.isPlayer) {
|
if (entity.isPlayer) {
|
||||||
if (!this.isMonster) {
|
this.seenTarget = undefined
|
||||||
console.log('oh ye, we safe')
|
|
||||||
} else {
|
|
||||||
console.log('hunt last seen location')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user