diff --git a/packages/behavior/traits/behaved.trait.js b/packages/behavior/traits/behaved.trait.js index 700cc60..1a7d6f1 100644 --- a/packages/behavior/traits/behaved.trait.js +++ b/packages/behavior/traits/behaved.trait.js @@ -33,6 +33,12 @@ export class Behaved extends decorate(Trait) { this.updateCurrentRoutine(); } + destroy() { + this._context.clear(); + this._currentRoutine = undefined; + this._routines = undefined; + } + get context() { return this._context; } diff --git a/packages/entity/index.js b/packages/entity/index.js index 9c55f4b..efea4e5 100644 --- a/packages/entity/index.js +++ b/packages/entity/index.js @@ -103,7 +103,7 @@ export class Entity extends decorate(Resource) { // Let the Trait do its initialization. instance.initialize(); // Attach listeners. - const listeners = instance.listeners(); + const listeners = instance.memoizedListeners(); for (const eventName in listeners) { this.on(eventName, listeners[eventName]); } @@ -243,7 +243,7 @@ export class Entity extends decorate(Resource) { delete this[property]; } // Remove all event listeners. - const listeners = instance.listeners(); + const listeners = instance.memoizedListeners(); for (const eventName in listeners) { this.off(eventName, listeners[eventName]); } @@ -251,6 +251,8 @@ export class Entity extends decorate(Resource) { this.state = this.state.delete(type); // Remove instance. delete this._traits[type]; + // Unloop. + // instance.entity = undefined; } removeTraits(types) { diff --git a/packages/entity/packet-synchronizer.js b/packages/entity/packet-synchronizer.js index 6798c2b..9aca93a 100644 --- a/packages/entity/packet-synchronizer.js +++ b/packages/entity/packet-synchronizer.js @@ -27,7 +27,7 @@ export class EntityPacketSynchronizer { const mergedPackets = this._mergePackets(packets); flusher(entity, Array.from(mergedPackets.values())); } - this.packetsToSynchronize = new Map(); + this.packetsToSynchronize.clear(); } _mergePackets(packets) { diff --git a/packages/entity/trait/index.js b/packages/entity/trait/index.js index b8c11d4..e7839bc 100644 --- a/packages/entity/trait/index.js +++ b/packages/entity/trait/index.js @@ -16,6 +16,7 @@ export class Trait extends decorate(class {}) { this.entity = entity; const ctor = this.constructor; this.isDirty = true; + this._memoizedListeners = undefined; this.params = I.fromJS(ctor.defaultParams()).merge(I.fromJS(params)); this.state = I.fromJS(ctor.defaultState()).merge(I.fromJS(state)); } @@ -50,6 +51,13 @@ export class Trait extends decorate(class {}) { return {}; } + memoizedListeners() { + if (!this._memoizedListeners) { + this._memoizedListeners = this.listeners(); + } + return this._memoizedListeners; + } + methods() { return {}; } diff --git a/packages/entity/traits/positioned.trait.js b/packages/entity/traits/positioned.trait.js index 371657e..d0af8e8 100644 --- a/packages/entity/traits/positioned.trait.js +++ b/packages/entity/traits/positioned.trait.js @@ -24,23 +24,7 @@ class PositionedBase extends Trait { } initialize() { - this.on('_positionChanged', (oldPosition, newPosition) => { - if (AVOCADO_SERVER) { - const x = Math.floor(newPosition[0]) * 4; - const y = Math.floor(newPosition[1]) * 4; - this.state = this.state.withMutations((state) => { - state.set('x', x).set('y', y); - }); - this.isDirty = true; - } - if (oldPosition[0] !== newPosition[0]) { - this.entity.emit('xChanged', oldPosition[0], newPosition[0]); - } - if (oldPosition[1] !== newPosition[1]) { - this.entity.emit('yChanged', oldPosition[1], newPosition[1]); - } - this.entity.emit('positionChanged', oldPosition, newPosition); - }); + this.on('_positionChanged', this.on_positionChanged, this); const x = this.state.get('x') / 4; const y = this.state.get('y') / 4; this._position = [x, y]; @@ -48,12 +32,39 @@ class PositionedBase extends Trait { this._relaxServerPositionConstraintIfNearerThan = 0; this.serverPosition = this._position; this.serverPositionDirty = false; - this.on('serverPositionChanged', () => { - this.serverPositionDirty = true; - }); + this.on('serverPositionChanged', this.onServerPositionChanged, this); } } + destroy() { + this.off('_positionChanged', this.on_positionChanged); + if (AVOCADO_CLIENT) { + this.off('serverPositionChanged', this.onServerPositionChanged); + } + } + + on_positionChanged(oldPosition, newPosition) { + if (AVOCADO_SERVER) { + const x = Math.floor(newPosition[0]) * 4; + const y = Math.floor(newPosition[1]) * 4; + this.state = this.state.withMutations((state) => { + state.set('x', x).set('y', y); + }); + this.isDirty = true; + } + if (oldPosition[0] !== newPosition[0]) { + this.entity.emit('xChanged', oldPosition[0], newPosition[0]); + } + if (oldPosition[1] !== newPosition[1]) { + this.entity.emit('yChanged', oldPosition[1], newPosition[1]); + } + this.entity.emit('positionChanged', oldPosition, newPosition); + } + + onServerPositionChanged() { + this.serverPositionDirty = true; + } + patchStateStep(key, step) { if ('state' !== key) { return; diff --git a/packages/entity/traits/spawner.trait.js b/packages/entity/traits/spawner.trait.js index 0bdfcff..1e0696d 100644 --- a/packages/entity/traits/spawner.trait.js +++ b/packages/entity/traits/spawner.trait.js @@ -72,7 +72,7 @@ export class Spawner extends decorate(Trait) { } const spawn = new Entity().fromJSON(merge(spawnJSON, json)); this.children.push(spawn); - spawn.on('destroy', () => { + spawn.once('destroy', () => { const index = this.children.indexOf(spawn); this.children.splice(index, 1); }) diff --git a/packages/graphics/container.js b/packages/graphics/container.js index 8dccf64..db96e30 100644 --- a/packages/graphics/container.js +++ b/packages/graphics/container.js @@ -30,12 +30,12 @@ export class Container extends Renderable { } destroy() { + this.container.filters = []; this.children.forEach((child) => { this.removeChild(child); child.destroy(); }); super.destroy(); - this.container.filters = []; } get internal() { diff --git a/packages/graphics/sprite.js b/packages/graphics/sprite.js index 2371db9..d1bf427 100644 --- a/packages/graphics/sprite.js +++ b/packages/graphics/sprite.js @@ -11,10 +11,6 @@ export class Sprite extends Renderable { this.anchor = [0.5, 0.5]; } - destroy() { - this.sprite.destroy(); - } - get internal() { return this.sprite; } diff --git a/packages/graphics/traits/emitter.trait.js b/packages/graphics/traits/emitter.trait.js index e378491..676ccc5 100644 --- a/packages/graphics/traits/emitter.trait.js +++ b/packages/graphics/traits/emitter.trait.js @@ -62,6 +62,7 @@ export class Emitter extends decorate(Trait) { this.ticker.tick(elapsed); if (!emitter.hasParticles()) { emitter.destroy(); + this.constructor.removeEmitter(emitter); delete this.emitters[key]; } } diff --git a/packages/graphics/traits/pictured.trait.js b/packages/graphics/traits/pictured.trait.js index d24c750..f326247 100644 --- a/packages/graphics/traits/pictured.trait.js +++ b/packages/graphics/traits/pictured.trait.js @@ -35,6 +35,7 @@ export class Pictured extends decorate(Trait) { for (const key in this.sprites) { this.hideImage(key); const sprite = this.sprites[key]; + sprite.image.destroy(); sprite.destroy(); } } diff --git a/packages/math/quadtree.js b/packages/math/quadtree.js index ce7f64f..05a52bf 100644 --- a/packages/math/quadtree.js +++ b/packages/math/quadtree.js @@ -12,6 +12,14 @@ export class QuadTree { this.quadTree.add(datum); } + clear() { + const data = this.quadTree.data(); + for (let i = 0; i < data.length; i++) { + const datum = data[i]; + this.quadTree.remove(datum); + } + } + remove(datum) { this.quadTree.remove(datum); } diff --git a/packages/physics/traits/shaped.trait.js b/packages/physics/traits/shaped.trait.js index a4dd23f..cd65ff6 100644 --- a/packages/physics/traits/shaped.trait.js +++ b/packages/physics/traits/shaped.trait.js @@ -24,6 +24,7 @@ export class Shaped extends decorate(Trait) { if (this.shapeView) { this.shapeView.destroy(); } + this.shapeView = undefined; } get shape() { diff --git a/packages/topdown/traits/followed.trait.js b/packages/topdown/traits/followed.trait.js index c4844be..363cb7d 100644 --- a/packages/topdown/traits/followed.trait.js +++ b/packages/topdown/traits/followed.trait.js @@ -18,14 +18,20 @@ export class Followed extends Trait { this.onRoomSizeChanged(); } + destroy() { + if (!this.entity.is('roomed')) { + const room = this.entity.room; + if (room) { + room.off('sizeChanged', this.onRoomSizeChanged); + } + } + } + get camera() { return this._camera; } onRoomSizeChanged() { - if (!this.entity.is('roomed')) { - return; - } this._camera.areaSize = this.entity.room.size; } @@ -39,7 +45,6 @@ export class Followed extends Trait { listeners() { return { - // TODO won't catch initial room size changes. addedToRoom: () => { this.onRoomSizeChanged(); this.entity.room.on('sizeChanged', this.onRoomSizeChanged, this);