Add senses and use animal definitions
This commit is contained in:
parent
22fe8e9fb1
commit
a9f2a8824e
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user