Source: core/factory.js

/** 
 * Abstract factory, extended by BubbleFactory, EnemyFleet, BonusFactory etc - provides basic factory operations.
 * Timed auto creation option, random timer
 *
 * @author Matthew Page <work@mjp.co>
 * @property {SpaceInvaders} game - Main game instance
 * @property {number} makeCounter - Count of how many instances have been made by this factory
 * @property {array} items - Store the items created in here.
 * @property {number} maxItems - Maximum number of items in the store
 * @property {number} minDelay - Minimum delay before auto creating
 * @property {number} maxDelay - Maximum delay before auto creating
 * @property {number} countdown - Countdown till the next auto create
 */
class Factory {
	/**
	 * Make the factory instance
	 *
	 * @param {SpaceInvaders} game - Main game instance
	 * @param {number} maxItems - Maximum number of items in the store
	 * @param {number} minDelay - Minimum delay before auto creating
	 * @param {number} maxDelay - Maximum delay before auto creating
	 */
	constructor(game, maxItems, minDelay, maxDelay) {
		this.game = game;
		this.makeCounter = 0;
		this.items = [];
		this.maxItems = maxItems;
		this.minDelay = minDelay;
		this.maxDelay = maxDelay;
		this.countdown = 0;
		this.resetCountdown();
	}
	/**
	 * Restart the auto make countdown based on max / min delay 
	 *
	 */
	resetCountdown() {
		this.countdown = Math.floor(Math.random() * (this.maxDelay-this.minDelay)) + this.minDelay;
	}
	/**
	 * Usuall method called to make a new item in this factory 
	 *
	 */
	make() {
		if(this.canMake()) {
			this.makeCounter += 1;
			this.items.push(this.makeItem());
			return true;	
		} else {
			return false;
		}
	}
	/**
	 * Make the actual item, called by the make() method
	 *
	 * @returns {Object} Returns the instance created
	 */
	makeItem() {
		// Always overide by parent class
		// return new Object(x,y,z);
		// EnemyFleet.makeItem -> return new EmenyShip(x,y,z)
	}
	/**
	 * Check if we can make a new instance based on maxItems
	 *
	 * @returns {boolean}
	 */
	canMake() {
		return (this.maxItems > 0 && this.items.length < this.maxItems)?true:false;
	}
	/**
	 * Destroy an instance created by this factory
	 *
	 * @param {Object} item - The item / instance to destroy
	 * @returns {Object} Returns the instance created
	 */
	destroy(item) {		
		/* Loop the array in reverse to enable splicing out elements */
		for(let index=this.items.length-1; index >= 0; index--) {
			if(this.items[index].id == item.id) {
				this.items[index].isActive = false;
				this.items[index].destroyDomElement();  // ???????????????????
				this.items.splice(index, 1);
				return true;
			}
		} 
		return false;
	}
	/**
	 * Main update loop
	 *
	 */
	update() {
		/* Auto create on random time settings (BonusDrops) */
		if(this.maxDelay > 0) {
			this.countdown -= 1;
			if(this.countdown < 0) {
				this.make();
				this.resetCountdown();
			}
		}
		/* Call update() on all the items, or destroy if not active */
		this.items.forEach((item, index) => {
			if(item.isActive) {
				item.update();
			} else {
				this.destroy(item);	
			}
		});
		return true;
	}
	/**
	 * Destroy all items and reset the data
	 *
	 */
	reset() {
		this.items.forEach(function(item, index) {
			item.domElement.parentNode.removeChild(item.domElement);
		});
		this.items = [];
		this.makeCounter = 0;
		this.resetCountdown();
	}
	
}