import {compose, merge} from '@avocado/core'; import {Vector} from '@avocado/math'; import {StateProperty, Trait} from '../trait'; import {Entity} from '..'; const decorate = compose( StateProperty('isSpawning', { track: true, }), StateProperty('maxSpawns'), ); export class Spawner extends decorate(Trait) { static defaultParams() { return { spawns: {}, }; } static defaultState() { return { isSpawning: true, maxSpawns: Infinity, }; } static type() { return 'spawner'; } destroy() { while (this.children.length > 0) { this.removeChild(this.children.pop()); } } constructor(entity, params, state) { super(entity, params, state); this.children = []; this.childrenListeners = new Map(); this.spawnJSONs = this.params.spawns; } destinationEntityList() { if ( this.entity.is('listed') && this.entity.list ) { return this.entity.list; } if ( this.entity.wielder && this.entity.wielder.is('listed') && this.entity.wielder.list ) { return this.entity.wielder.list; } } maySpawn() { if (this.maxSpawns <= this.children.length) { return false; } return true; } removeChild(child) { const index = this.children.indexOf(child); if (-1 !== index) { this.children.splice(index, 1); const listener = this.childrenListeners.get(child); child.off('destroy', listener); this.childrenListeners.delete(child); } } listeners() { return { dying: () => { this.isSpawning = false; }, }; } methods() { return { killAllChildren: () => { // Juggle children since this may cause splices and mess up the array. const children = this.children.slice(0); for (let i = 0; i < children.length; i++) { children[i].destroyGently(); } }, spawn: (key, json = {}) => { if (!this.maySpawn()) { return; } const spawnJSON = this.spawnJSONs[key]; if (!spawnJSON) { return; } const list = this.destinationEntityList(); if (!list) { return; } const mergedJSON = merge({}, spawnJSON, json); // Add null to children to prevent race. const childIndex = this.children.length; this.children.push(null); return Entity.loadOrInstance(mergedJSON).then((child) => { this.children[childIndex] = child; const listener = this.removeChild.bind(this, child); this.childrenListeners.set(child, listener); child.once('destroy', listener); list.addEntity(child); return child; }); }, spawnAt: (key, position, json = {}) => { if (true && !this.maySpawn()) { return; } if (!json.traits) { json.traits = {}; } if (!json.traits.positioned) { json.traits.positioned = {}; } if (!json.traits.positioned.state) { json.traits.positioned.state = {}; } json.traits.positioned.state.x = position[0]; json.traits.positioned.state.y = position[1]; return this.entity.spawn(key, json); }, }; } }