diff --git a/Engine/src/live/Entity.ts b/Engine/src/live/Entity.ts index 971e828..7eaf807 100644 --- a/Engine/src/live/Entity.ts +++ b/Engine/src/live/Entity.ts @@ -1,5 +1,7 @@ import * as planck from 'planck' import { SpriteInstance } from '../shared/sprites' +import { WorldContext } from './World' +import { Zone } from './Zone' export class Entity { sprite: SpriteInstance @@ -9,13 +11,15 @@ export class Entity { maxSpeed: number = 4 direction: number = 0 turnRate: number = 10 + zones: Zone[] = [] + contacts: Entity[] = [] constructor(ctor: string) { this.sprite = new SpriteInstance(ctor) } - update(delta: number) { - // TODO: Update sprite. + update(delta: number, ctx?: WorldContext) { + this.sprite.update(delta) } get position(): [number, number] { @@ -65,4 +69,18 @@ export class Entity { this.sprite.container.y = v } } + addZoneContact(zone: Zone) { + if (this.zones.find(v=>v===zone)) return + this.zones.push(zone) + } + removeZoneContact(zone: Zone) { + this.zones = this.zones.filter(v=>v!==zone) + } + addEntityContact(entity: Entity) { + if (this.contacts.find(v=>v===entity)) return + this.contacts.push(entity) + } + removeEntityContact(entity: Entity) { + this.contacts = this.contacts.filter(v=>v!==entity) + } } diff --git a/Engine/src/live/PlayerEntity.ts b/Engine/src/live/PlayerEntity.ts index 0580438..d67546d 100644 --- a/Engine/src/live/PlayerEntity.ts +++ b/Engine/src/live/PlayerEntity.ts @@ -1,9 +1,12 @@ import * as planck from 'planck' import { Action } from "./Action" import { Entity } from "./Entity" +import { PuddleEntity } from './PuddleEntity' +import { WorldContext } from './World' export class PlayerEntity extends Entity { action?: Action + puddleTimer: number = 0 constructor(ctor: string) { super(ctor) } @@ -18,10 +21,23 @@ export class PlayerEntity extends Entity { return 0 })[0] } - update(delta: number) { + 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) { + ctx.addEntity(new PuddleEntity('effects.water.ripple.small.0'), this.x, this.y) + console.log('add ripple') + } + this.puddleTimer = 0 + } + } + let shouldMove = false if (this.action) { // FIXME: Use physics. - let shouldMove = false switch(this.action.type) { case 'west': if (this.direction !== 0) { @@ -69,19 +85,26 @@ export class PlayerEntity extends Entity { } else if (this.direction < 0) { this.direction = 360 } - 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[1]) < this.maxSpeed) { - this.velocity[1] -= Math.sin(r) * this.acceleration - } - let cardinal = this.getCardinal() - if (this.sprite.subsetKey !== cardinal) { - this.sprite.setCtor(`${this.sprite.spriteKey}.${this.sprite.animationKey}.${this.sprite.setKey}.${cardinal}.${this.sprite.frameIndex}`) - } + } + + 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[1]) < this.maxSpeed) { + this.velocity[1] -= Math.sin(r) * this.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`) } // @@ -92,25 +115,17 @@ export class PlayerEntity extends Entity { this.body?.setLinearVelocity(planck.Vec2(this.velocity[0], this.velocity[1])) } getCardinal(): string { - const degreesPerDirection = 360 / 8 + const degreesPerDirection = 360 / 4 const angle = this.direction + degreesPerDirection / 2 if (angle >= 0 * degreesPerDirection && angle < 1 * degreesPerDirection) { - return 'w' + return 'west' } else if (angle >= 1 * degreesPerDirection && angle < 2 * degreesPerDirection) { - return 'nw' + return 'north' } else if (angle >= 2 * degreesPerDirection && angle < 3 * degreesPerDirection) { - return 'n' - } else if (angle >= 3 * degreesPerDirection && angle < 4 * degreesPerDirection) { - return 'ne' - } else if (angle >= 4 * degreesPerDirection && angle < 5 * degreesPerDirection) { - return 'e' - } else if (angle >= 5 * degreesPerDirection && angle < 6 * degreesPerDirection) { - return 'se' - } else if (angle >= 6 * degreesPerDirection && angle < 7 * degreesPerDirection) { - return 's' + return 'east' } - return 'sw' + return 'south' } } diff --git a/Engine/src/live/PuddleEntity.ts b/Engine/src/live/PuddleEntity.ts new file mode 100644 index 0000000..210bbda --- /dev/null +++ b/Engine/src/live/PuddleEntity.ts @@ -0,0 +1,20 @@ +import { Entity } from "./Entity" +import { WorldContext } from "./World" + +export class PuddleEntity extends Entity { + lifetime: number = 400 + elapsed: number = 0 + constructor(ctor: string) { + super(ctor) + console.log(this.sprite) + } + update(delta: number, ctx?: WorldContext) { + super.update(delta) + this.elapsed += delta + if (this.elapsed >= this.lifetime) { + console.log('ah, remove ourself') + ctx?.removeEntity(this) + } + } + +} \ No newline at end of file diff --git a/Engine/src/live/World.ts b/Engine/src/live/World.ts new file mode 100644 index 0000000..9ac2bc4 --- /dev/null +++ b/Engine/src/live/World.ts @@ -0,0 +1,6 @@ +import { Entity } from "./Entity" + +export interface WorldContext { + addEntity(entity: Entity, x: number, y: number): void + removeEntity(entity: Entity): void +} \ No newline at end of file diff --git a/Engine/src/shared/sprites.ts b/Engine/src/shared/sprites.ts index 4fd35a4..aa2051e 100644 --- a/Engine/src/shared/sprites.ts +++ b/Engine/src/shared/sprites.ts @@ -20,6 +20,9 @@ export class SpriteInstance { subsetKey: string = '' frame?: SpritePart frameIndex: number = 0 + frameCount: number = 0 + animate: boolean = true + elapsed: number = 0 constructor(ctor: string) { this.container = new PIXI.Container() @@ -34,6 +37,7 @@ export class SpriteInstance { this.setKey = setKey this.subsetKey = subsetKey this.frameIndex = Number(frameKey) + this.frameCount = 0 this.sprite = sprites[spriteKey] if (!this.sprite) { this.container.addChild(new PIXI.Text('missing sprite')) @@ -44,6 +48,7 @@ export class SpriteInstance { this.subset = this.set.children[subsetKey] // if (this.subset.frames) { + this.frameCount = this.subset.frames.length this.frame = this.subset.frames[this.frameIndex] let s = new PIXI.Sprite(this.frame.texture) s.x -= this.frame.originX @@ -52,6 +57,19 @@ export class SpriteInstance { } } + update(delta: number) { + if (!this.animate) return + this.elapsed += delta + while (this.frameCount > 0 && this.frame && this.elapsed >= this.frame.time && this.frame.time > 0) { + this.elapsed -= this.frame.time + this.frameIndex++ + if (this.frameIndex >= this.frameCount) { + this.frameIndex = 0 + } + this.setCtor(`${this.spriteKey}.${this.animationKey}.${this.setKey}.${this.subsetKey}.${this.frameIndex}`) + } + } + getBodyShape(): Shape|undefined { if (this.frame && this.frame.shapes) { return this.frame.shapes.find(v=>v.key==='body') diff --git a/Engine/src/states/Game.ts b/Engine/src/states/Game.ts index 1eaf30b..26b4831 100644 --- a/Engine/src/states/Game.ts +++ b/Engine/src/states/Game.ts @@ -12,6 +12,7 @@ import { Zone } from "../live/Zone" import { Entity } from "../live/Entity" import { isPlayerEntity, PlayerEntity } from "../live/PlayerEntity" import { Action } from "../live/Action" +import { WorldContext } from "../live/World" export interface PIXIMissingColorMatrix extends PIXI.Filter { night(intensity: number, multiply: boolean): void @@ -34,8 +35,9 @@ export function GameState(ctx: ContextI): StateI { //disableSound() let isNight = false let modeTimer = 0 - let nightTime = 10 * 60 - let dayTime = 10 * 60 + let nightTime = 10 * 1000 + let dayTime = 10 * 1000 + let lastTime: number = performance.now() let world: planck.World = planck.World({ gravity: planck.Vec2(0, 0), @@ -43,6 +45,34 @@ export function GameState(ctx: ContextI): StateI { let worldBody: planck.Body = world.createBody({ type: 'static', }) + world.on('begin-contact', (contact: planck.Contact) => { + let a = contact.getFixtureA().getUserData() + let b = contact.getFixtureB().getUserData() + if (a instanceof Zone) { + if (b instanceof Entity) { + b.addZoneContact(a) + } + } else if (a instanceof Entity) { + if (b instanceof Entity) { + a.addEntityContact(b) + b.addEntityContact(a) + } + } + }) + world.on('end-contact', (contact: planck.Contact) => { + let a = contact.getFixtureA().getUserData() + let b = contact.getFixtureB().getUserData() + if (a instanceof Zone) { + if (b instanceof Entity) { + b.removeZoneContact(a) + } + } else if (a instanceof Entity) { + if (b instanceof Entity) { + a.removeEntityContact(b) + b.removeEntityContact(a) + } + } + }) let rootContainer = new PIXI.Container() let playLayer: Layer @@ -51,6 +81,7 @@ export function GameState(ctx: ContextI): StateI { let zones: Zone[] = [] let enter = () => { + lastTime = performance.now() hookKeyboard() // Load the world segment. let w = segments.world @@ -115,7 +146,7 @@ export function GameState(ctx: ContextI): StateI { } // Add bogus entity - addEntity(new PlayerEntity('bogus-arrows.player.normal.w.0'), 0, 0) + addEntity(new PlayerEntity('deer.animal.stand.west.0'), 300, 100) ctx.app.stage.addChild(rootContainer) } @@ -128,9 +159,12 @@ export function GameState(ctx: ContextI): StateI { } let elapsed: number = 0 let update = (delta: number) => { + let time = performance.now() + let realDelta = time - lastTime + lastTime = time checkGamepads() elapsed += delta - modeTimer += delta + modeTimer += realDelta if (isNight && modeTimer >= nightTime) { modeTimer = 0 nightfall(false) @@ -146,7 +180,7 @@ export function GameState(ctx: ContextI): StateI { // Update/render. for (let layer of layers) { for (let decoration of layer.decorations) { - decoration.update(delta) + decoration.update(realDelta) } } for (let entity of entities) { @@ -156,7 +190,7 @@ export function GameState(ctx: ContextI): StateI { 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))) } - entity.update(delta) + entity.update(realDelta, worldContext) // I guess... entity.sprite.container.x = entity.x entity.sprite.container.y = entity.y @@ -166,6 +200,7 @@ export function GameState(ctx: ContextI): StateI { let addEntity = (entity: Entity, x: number, y: number) => { if (entities.find(v=>v===entity)) return + console.log('add somethin', entity) entities.push(entity) playLayer.container.addChild(entity.sprite.container) // I guess this is a fair enough place to create physics and add it to the entity. @@ -188,6 +223,7 @@ export function GameState(ctx: ContextI): StateI { friction: 0.9, restitution: 0.05, }) + fixture.setUserData(entity) entity.body = body body.setUserData(entity) } @@ -214,6 +250,7 @@ export function GameState(ctx: ContextI): StateI { if (zone.type === 'fluid') { zone.fixture.setSensor(true) } + zone.fixture.setUserData(zone) } let removeZone = (zone: Zone) => { zones = zones.filter(v=>v!==zone) @@ -222,6 +259,11 @@ export function GameState(ctx: ContextI): StateI { } } + let worldContext: WorldContext = { + addEntity, + removeEntity, + } + let desiredActions: Action[] = [] let adjustAction = (type: string, v: number, inc?: boolean) => { let action = desiredActions.find(v=>v.type===type)