Add basic animal AI

This commit is contained in:
Ketchetwahmeegwun T. Southall 2022-01-30 00:24:56 -08:00
parent 9ab05fe40b
commit a9c8114096

View File

@ -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,18 +148,50 @@ 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
@ -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')
}
} }
} }
} }