Add senses and use animal definitions

This commit is contained in:
Ketchetwahmeegwun T. Southall 2022-01-29 23:48:13 -08:00
parent 22fe8e9fb1
commit a9f2a8824e
2 changed files with 153 additions and 25 deletions

View File

@ -1,6 +1,7 @@
import * as planck from 'planck'
import { AnimalDefinition, CreatureDefinition } from '../data/animals'
import { Action } from "./Action"
import { Entity } from "./Entity"
import { Entity, Sensor } from "./Entity"
import { PuddleEntity } from './PuddleEntity'
import { WorldContext } from './World'
@ -8,9 +9,19 @@ export class AnimalEntity extends Entity {
action?: Action
puddleTimer: number = 0
isPlayer: boolean = false
isMonster: boolean = false
thinkTimer: number = 0 // Time since last thought, used by AI.
target?: Entity
def: AnimalDefinition
mode: CreatureDefinition
constructor(ctor: string) {
super(ctor)
constructor(def: AnimalDefinition) {
super(`${def.name}.animal.stand.west.0`)
this.def = def
this.mode = def.animal
this.maxSpeed = def.animal.maxSpeed
this.acceleration = def.animal.acceleration
this.turnRate = def.animal.turnRate
}
act(actions: Action[]) {
this.action = actions.sort((a, b) => {
@ -46,9 +57,9 @@ export class AnimalEntity extends Entity {
case 'west':
if (this.direction !== 0) {
if (this.direction < 180) {
this.direction -= this.turnRate
this.direction -= this.mode.turnRate
} else {
this.direction += this.turnRate
this.direction += this.mode.turnRate
}
}
shouldMove = true
@ -56,9 +67,9 @@ export class AnimalEntity extends Entity {
case 'east':
if (this.direction !== 180) {
if (this.direction < 180) {
this.direction += this.turnRate
this.direction += this.mode.turnRate
} else {
this.direction -= this.turnRate
this.direction -= this.mode.turnRate
}
}
shouldMove = true
@ -66,9 +77,9 @@ export class AnimalEntity extends Entity {
case 'north':
if (this.direction !== 90) {
if (this.direction < 90 || this.direction > 270) {
this.direction += this.turnRate
this.direction += this.mode.turnRate
} else {
this.direction -= this.turnRate
this.direction -= this.mode.turnRate
}
}
shouldMove = true
@ -76,9 +87,9 @@ export class AnimalEntity extends Entity {
case 'south':
if (this.direction !== 270) {
if (this.direction > 90 && this.direction < 270) {
this.direction += this.turnRate
this.direction += this.mode.turnRate
} else {
this.direction -= this.turnRate
this.direction -= this.mode.turnRate
}
}
shouldMove = true
@ -93,11 +104,11 @@ export class AnimalEntity extends Entity {
if (shouldMove) {
let r = this.direction * (Math.PI/180)
if (Math.abs(this.velocity[0]) < this.maxSpeed) {
this.velocity[0] -= Math.cos(r) * this.acceleration
if (Math.abs(this.velocity[0]) < this.mode.maxSpeed) {
this.velocity[0] -= Math.cos(r) * this.mode.acceleration
}
if (Math.abs(this.velocity[1]) < this.maxSpeed) {
this.velocity[1] -= Math.sin(r) * this.acceleration
if (Math.abs(this.velocity[1]) < this.mode.maxSpeed) {
this.velocity[1] -= Math.sin(r) * this.mode.acceleration
}
let cardinal = this.getCardinal()
this.sprite.setKey = 'run'
@ -131,6 +142,73 @@ export class AnimalEntity extends Entity {
}
return 'south'
}
think(delta: number) {
this.thinkTimer += delta
while (this.thinkTimer >= 500) {
if (!this.isMonster) {
if (this.target) {
// Flee from target
} else {
// Wander.
}
}
this.thinkTimer -= 500
}
}
setTarget(entity: Entity) {
}
sense(sensor: Sensor, entity: AnimalEntity) {
if (this.isPlayer) return
if (sensor.type === 'long') {
this.smell(entity)
} else {
this.see(entity)
}
}
lost(sensor: Sensor, entity: AnimalEntity) {
if (this.isPlayer) return
if (sensor.type === 'long') {
this.unsmell(entity)
} else {
this.unsee(entity)
}
}
smell(entity: AnimalEntity) {
if (entity.isPlayer) {
if (!this.isMonster) {
console.log('alert, we can smell player')
} else {
console.log('we smell the player, wander towards them')
}
}
}
unsmell(entity: AnimalEntity) {
if (entity.isPlayer) {
if (!this.isMonster) {
console.log('no longer smell player, wander around')
} else {
console.log('no longer smell player, wander around')
}
}
}
see(entity: AnimalEntity) {
if (entity.isPlayer) {
if (!this.isMonster) {
console.log('oh no, run from player')
} else {
console.log('oh ho, chase player')
}
}
}
unsee(entity: AnimalEntity) {
if (entity.isPlayer) {
if (!this.isMonster) {
console.log('oh ye, we safe')
} else {
console.log('hunt last seen location')
}
}
}
}
export function isAnimalEntity(o: any): o is AnimalEntity {

View File

@ -9,10 +9,11 @@ import { SpriteInstance, sprites } from "../shared/sprites"
import { ShapeCircle, ShapePoints } from "../data/sprite"
import { SegmentZone } from "../data/segment"
import { Zone } from "../live/Zone"
import { Entity } from "../live/Entity"
import { Entity, Sensor } from "../live/Entity"
import { AnimalEntity, isAnimalEntity } from "../live/AnimalEntity"
import { Action } from "../live/Action"
import { WorldContext } from "../live/World"
import { animals } from "../data/animals"
export interface PIXIMissingColorMatrix extends PIXI.Filter {
night(intensity: number, multiply: boolean): void
@ -38,6 +39,7 @@ export function GameState(ctx: ContextI): StateI {
let nightTime = 30 * 1000
let dayTime = 30 * 1000
let lastTime: number = performance.now()
let player: AnimalEntity
let world: planck.World = planck.World({
gravity: planck.Vec2(0, 0),
@ -58,6 +60,16 @@ export function GameState(ctx: ContextI): StateI {
b.addEntityContact(a)
}
}
if (a instanceof Sensor && a.entity instanceof AnimalEntity) {
if (b instanceof AnimalEntity) {
a.entity.sense(a, b)
}
}
if (b instanceof Sensor && b.entity instanceof AnimalEntity) {
if (a instanceof AnimalEntity) {
b.entity.sense(b, a)
}
}
})
world.on('end-contact', (contact: planck.Contact) => {
let a = contact.getFixtureA().getUserData()
@ -72,6 +84,16 @@ export function GameState(ctx: ContextI): StateI {
b.removeEntityContact(a)
}
}
if (a instanceof Sensor && a.entity instanceof AnimalEntity) {
if (b instanceof AnimalEntity) {
a.entity.lost(a, b)
}
}
if (b instanceof Sensor && b.entity instanceof AnimalEntity) {
if (a instanceof AnimalEntity) {
b.entity.lost(b, a)
}
}
})
let rootContainer = new PIXI.Container()
@ -145,14 +167,13 @@ export function GameState(ctx: ContextI): StateI {
addZone(new Zone(z))
}
// Add bogus entity
let player = new AnimalEntity('deer.animal.stand.west.0')
// Add player entity
player = new AnimalEntity(animals.deer)
player.isPlayer = true
addEntity(player, 'objects', 300, 100)
// Add fake others
addEntity(new AnimalEntity('deer.animal.stand.west.0'), 'objects', 200, 200)
addEntity(new AnimalEntity(animals.turkey), 'objects', 200, 200)
ctx.app.stage.addChild(rootContainer)
}
@ -190,11 +211,21 @@ export function GameState(ctx: ContextI): StateI {
}
}
for (let entity of entities) {
if (isAnimalEntity(entity) && entity.isPlayer) {
entity.act(desiredActions)
// FIXME: This isn't the right place for this.
rootContainer.position.set(Math.round(ctx.app.renderer.width/2), Math.round(ctx.app.renderer.height/2))
rootContainer.pivot.set(Math.max(rootContainer.width/6, Math.min(entity.x, rootContainer.width/3)), Math.max(rootContainer.height/6, Math.min(entity.y, rootContainer.height/3)))
if (isAnimalEntity(entity)) {
if (entity.isPlayer) {
entity.act(desiredActions)
// FIXME: This isn't the right place for this.
rootContainer.position.set(Math.round(ctx.app.renderer.width/2), Math.round(ctx.app.renderer.height/2))
rootContainer.pivot.set(Math.max(rootContainer.width/6, Math.min(entity.x, rootContainer.width/3)), Math.max(rootContainer.height/6, Math.min(entity.y, rootContainer.height/3)))
} else {
//if (entity.body?.)
// Some sort of simple AI.
if (!entity.isMonster) {
// Slowly wander and run from player if close.
} else {
// Heavily wander and run towards player if close.
}
}
} else {
//console.log('hmm, we do be tickin', entity, entity.x, entity.y)
}
@ -237,6 +268,25 @@ export function GameState(ctx: ContextI): StateI {
fixture.setUserData(entity)
entity.body = body
body.setUserData(entity)
// Sensors
let defaultShort = 50
let defaultLong = 100
if (entity instanceof AnimalEntity) {
defaultLong = entity.def.animal.scent
defaultShort = entity.def.animal.sight
}
// Create a short sensor (vision).
let senseFixture = body.createFixture({
shape: planck.Circle(planck.Vec2(entity.x, entity.y), defaultShort),
isSensor: true,
})
senseFixture.setUserData(new Sensor(entity, senseFixture, 'short'))
// Create a long sensor (scent).
let longSenseFixture = body.createFixture({
shape: planck.Circle(planck.Vec2(entity.x, entity.y), defaultLong),
isSensor: true,
})
longSenseFixture.setUserData(new Sensor(entity, longSenseFixture, 'long'))
}
}
entity.x = x