Source: enemy/enemy-ship.js

/** 
 * The basic enemy ship, bomb countdown, movement logic etc. Extended by other ship types. 
 *
 * @author Matthew Page <work@mjp.co>
 * @extends Sprite
 * @property {SpaceInvaders} game
 * @property {EnemyFleet} fleet - The Enemy Fleet factory instance.
 * @property {number} scoreValue - Score player gets for shooting this ship down.
 * @property {number} rotation - Rotation angle for death animation.
 * @property {number} lastDirection - The last direction the ship was moving.
 * @property {number} moveCounter - Counter used in the movement logic.
 * @property {boolean} isDying - Is this ship in its death animation.
 * @property {number} maxBombReload - Max number of frames before another bomb can be dropped.
 */
class EnemyShip extends Sprite {
	/**
	* Create an enemy ship.
	*
	* @param {SpaceInvaders} game - The current game.
	* @param {EnemyFleet} fleet - Factory that created this instance.
	* @param {EnemyBombFactory} bombFactory - Factory to create enemy bombs.
	* @param {number} startX - Starting X position.
	* @param {number} startY - Starting Y position.
	* @param {number} direction - Direction ship is moving.
	* @param {string} id - Unique DOM ID.
	* @param {string} css - Additional CSS class to be assigned to this ship.
	*/
	constructor(game, fleet, bombFactory, startX, startY, direction, id, css) {
		/* Sprite(game, id, posX, posY, width, height, velocity, direction, hitPoints) */
		super(game, id, startX, startY, 40, 40, 1, direction, 1);
		this.fleet = fleet;
		this.bombFactory = bombFactory;
		this.scoreValue = 10;
		this.rotation = 0;
		this.lastDirection = direction;
		this.moveCounter = 0;
		this.isDying = false;
		this.makeDomElement('enemyShip');
		this.domElement = document.getElementById(this.id);
		this.domElement.classList.add(css);
		this.maxBombReload = 1500;
		this.startBombCountdown();
	}
	/**
	 * Reset the bomb counter to a random number, drops 1 each game loop.
	 * When it reaches 0 the update loop will trigger a new bomb to be 
	 * created from the EnemyBombFactory.
	 *
	 */
	startBombCountdown() {
		this.bombCountdown = Math.floor((Math.random() * this.maxBombReload) + 1);
	}
	/**
	 * Main update loop called every animation frame.
	 * Dying - If the ship is dying rotate and shrink it until width < 5
	 * Hit Points - If < 0 start the dying process
	 * Bomb launch - Check the countdown and if < 0 make new bomb
	 * Collision detection - player ship and shieldblock
	 *
	 * @returns {boolean} Success or failure.
	 */
	update() {

		if(this.isDying) {
			/* Ship has been hit and is in its dying animation, spin and shrink */
			this.rotation += 5;
			this.width -= 1;
			this.height -= 1;
			this.posY += 1;
			this.domElement.style.transform = "rotate("+this.rotation+"deg)";
			if(this.width < 5) {
				/* It has died, final object destruction */
				this.fleet.removeShip(this);
			}
		}
		else if(this.hitPoints<=0 && this.isActive) {
			/* Ship is alive, but hitPoints is at or below 0 - time to die */
			this.kill();
		}
		else
		{
			/* Bomb countdown logic */
			this.bombCountdown -= 1;
			if(this.bombCountdown==0) {
				this.bombFactory.make(this);
				this.startBombCountdown();
			}
			/* Move the ship */
			this.move();	

			/* Collision detection - inside player ship */
			if(this.detectCollisionWith(this.game.player)) {
			   	this.game.player.receiveDamage(1000);
				this.isDying = true;
				return true;
			}

			/* Collision detection - inside shieldBlock */
			let blocksHit = this.game.shieldGrid.getShieldBlocksCollidingWith(this);
			blocksHit.forEach((shieldBlock, index) => {
				shieldBlock.receiveDamage(1000);
			});
			
			//console.log(`Shield Block Hit - ${shieldBlockHit}`);
			if(blocksHit.length>0) {
				//console.log('SHIELD HIT BY SHIP');
				this.isDying = true;
				return true;
			}
			
		}
		this.draw();
	}
	/**
	 * Process the movememnt logic - Move till edge, move down for 25 frames, move back other direction.
	 *
	 */
	move() {
		switch (this.direction) {
			case 90:										// Move right
				this.posX += this.velocity;
				if (this.posX >= 600)						// Reached end of display
				{
					this.posX = 600;
					this.lastDirection = this.direction;	// Remember the last direction
					this.direction = 180;					// New direction is down
					this.velocity = 2;						// New speed
					this.moveCounter = 0;					// Reset move counter to record how far down we go
				}
				break;
			case 180:										// Move down
				this.posY += this.velocity;
				this.moveCounter += 1;
				if(this.moveCounter > 25)
				{
					this.direction = (this.lastDirection == 90)?270:90;
					this.velocity = 1;
					this.moveCounter = 0;
				}
				break;
			case 270:
				this.posX -= this.velocity;
				if (this.posX <= 0)
				{
					this.posX = 0;
					this.lastDirection = this.direction;
					this.direction = 180;
					this.velocity = 2;
					this.moveCounter = 0;
				}
				break;
		}
	}
	/**
	 * Kills the enemy ship and starts the death animation loop, updates Score.
	 *
	 */
	kill() {
		this.isDying = true;
		new Audio('sfx/enemydie.mp3').play();
		this.game.score += this.scoreValue;
		this.game.bubbleFactory.make("score", this.scoreValue, this.posX, this.posY);
	}
}