Source: player/shield-grid.js

/*  */
/**
 * Wrapper and factory for the entire grid of shield blocks. Makes up the whole
 * shield. 
 * 0 - No shield
 * 1 - Standard shield block
 * 2 - Different colour shield block
 *
 * @author Matthew Page <work@mjp.co>
 * @extends Sprite
 * @property {array} shieldBlocks - The main shield grid array, stores all the shield blocks
 * @property {array} shieldPattern - A pattern to read and turn into the shield grid
 */
class ShieldGrid extends Sprite {
	/**
	 * Create a new shield grid
	 *
	 * @param {SpaceInvaders} game - The current game instance
	 */
	constructor(game) {
		/* Sprite(game, id, posX, posY, width, height, velocity, direction, hitPoints) */
		super(game, 'shieldGrid1', 0, 350, 640, 64, 0, 0, 1);

		this.shieldBlocks = [];
		this.shieldPattern = [
			[0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0],
			[0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,2,2,2,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0],
			[0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0],
			[0,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0]
		];
		this.makeDomElement('shieldGrid');
		this.domElement = document.getElementById(this.id);
		this.makeGrid(this.shieldPattern);
	}
	/**
	 * Make the grid pattern by creating new ShieldBlock instances
	 * Populates the shieldBlocks array from shieldPattern
	 *
	 * @param {array} pattern - The shield pattern array to make
	 */
	makeGrid(pattern) {
		this.shieldBlocks = [];
		/* Scan the shieldBlocks array and replace '1' or '2' with new ShieldBlock instances */
		for(let gridY=0; gridY<4; ++gridY) {
			this.shieldBlocks[gridY] = [];
			for(let gridX=0; gridX<40; ++gridX) {
				if(pattern[gridY][gridX]==1) {					
					this.shieldBlocks[gridY][gridX] = new ShieldBlock(this.game, this, gridX, gridY);
				}
				else if(pattern[gridY][gridX]==2) {					
					this.shieldBlocks[gridY][gridX] = new ShieldBlock(this.game, this, gridX, gridY);
					this.shieldBlocks[gridY][gridX].domElement.classList.add('strong');
					this.shieldBlocks[gridY][gridX].hitPoints = 1000; // not implemented in receiveDamage
				}
				else {
					this.shieldBlocks[gridY][gridX] = false;
				}
			}
		}
	}
	/**
	 * Reset the shield to default. Remove all the shield blocks from the
	 * shield grid HTML dom element.
	 *
	 */
	reset() {
		this.domElement.innerHTML = "";
		this.makeGrid(this.shieldPattern);
	}
	/**
	 * Return the ShieldBlock instance at the global pixel coord provided.
	 * Converts to the local shieldGrid coords (40x4 grid)
	 *
	 * @param {number} x - Global X pixel position
	 * @param {number} y - Global y pixel position
	 * @returns {ShieldBlock} Or false if position is empty
	 */
	getShieldBlockAt(x, y) {	
		if(this.inMe(x, y)) {
			let gridY = Math.floor( (y - this.posY)/16 );
			let gridX = Math.floor( x / 16 );
			return this.shieldBlocks[gridY][gridX];
		}
	}
	/**
	 * Return the ShieldBlock(s) that are colliding with the supplied
	 * Sprite instance. Uses the shieldGrid array to quickly find all
	 * the blocks touching the sprite
	 *
	 * @param {Sprite} sprite - The sprite instance colliding with the shield.
	 * @returns {ShieldBlock[]} Array of shieldBlocks in collision with sprite.
	 */
	getShieldBlocksCollidingWith(sprite) {
		let blocks = [];
		if(sprite.posY + sprite.height > this.posY) {
			/* The first row of the shield grid the sprite touches */
			let startY = Math.floor((sprite.posY-this.posY)/16);
			startY = startY < 0 ? 0 : startY;
			
			/* The last row of the shield grid the sprite touches */
			let endY = Math.ceil((sprite.posY+sprite.height-this.posY)/16);
			endY = endY < 0 ? 0 : endY;
			endY = endY > 3 ? 3 : endY;
			
			/* Loop through the shield grid */
			for(let gridY=startY; gridY<endY; ++gridY) {
				for(let gridX = Math.floor(sprite.posX/16); gridX <= Math.ceil((sprite.posX+sprite.width)/16); ++gridX) {					
					if(this.shieldBlocks[gridY][gridX]) {
						blocks.push(this.shieldBlocks[gridY][gridX]);
					}
				}
			}
		}
		return blocks;
	}
}