import * as planck from 'planck' import { AnimalDefinition, CreatureDefinition } from '../data/animals' import { Action } from "./Action" import { Entity, Sensor } from "./Entity" import { PuddleEntity } from './PuddleEntity' import { WorldContext } from './World' 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(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) => { if (a.priority < b.priority) { return 1 } if (a.priority > b.priority) { return -1 } return 0 })[0] } update(delta: number, ctx: WorldContext) { super.update(delta) let waterZones = this.zones.filter(v=>v.type==='fluid') if (waterZones.length) { this.puddleTimer += delta if (this.puddleTimer >= 600) { if (ctx) { let p = this.position p[0] += (this.sprite.frame?.originX??0) + ((this.sprite.frame?.width??0)/2??0) p[1] += (this.sprite.frame?.originY??0) + ((this.sprite.frame?.height??0)/2??0) ctx.addEntity(new PuddleEntity('effects.water.ripple.small.0'), 'ground', p[0], p[1]) } this.puddleTimer = 0 } } let shouldMove = false if (this.action) { // FIXME: Use physics. switch(this.action.type) { case 'west': if (this.direction !== 0) { if (this.direction < 180) { this.direction -= this.mode.turnRate } else { this.direction += this.mode.turnRate } } shouldMove = true break case 'east': if (this.direction !== 180) { if (this.direction < 180) { this.direction += this.mode.turnRate } else { this.direction -= this.mode.turnRate } } shouldMove = true break case 'north': if (this.direction !== 90) { if (this.direction < 90 || this.direction > 270) { this.direction += this.mode.turnRate } else { this.direction -= this.mode.turnRate } } shouldMove = true break case 'south': if (this.direction !== 270) { if (this.direction > 90 && this.direction < 270) { this.direction += this.mode.turnRate } else { this.direction -= this.mode.turnRate } } shouldMove = true break } if (this.direction > 360) { this.direction = 0 } else if (this.direction < 0) { this.direction = 360 } } if (shouldMove) { let r = this.direction * (Math.PI/180) 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.mode.maxSpeed) { this.velocity[1] -= Math.sin(r) * this.mode.acceleration } let cardinal = this.getCardinal() this.sprite.setKey = '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.animate = true } else { this.sprite.animate = false this.sprite.setKey = 'stand' this.sprite.setCtor(`${this.sprite.spriteKey}.${this.sprite.animationKey}.${this.sprite.setKey}.${this.getCardinal()}.0`) } // this.velocity[0] *= 0.65 this.velocity[1] *= 0.65 // Eh... let's manually handle velocity this.body?.setLinearVelocity(planck.Vec2(this.velocity[0], this.velocity[1])) } getCardinal(): string { const degreesPerDirection = 360 / 4 const angle = this.direction + degreesPerDirection / 2 if (angle >= 0 * degreesPerDirection && angle < 1 * degreesPerDirection) { return 'west' } else if (angle >= 1 * degreesPerDirection && angle < 2 * degreesPerDirection) { return 'north' } else if (angle >= 2 * degreesPerDirection && angle < 3 * degreesPerDirection) { return 'east' } 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 { return o.act }