// JS-Invaders main script

// constructors

function gameObject() {
	this.x=0;
	this.y=0;
	this.s=0
}

// globals
var gameTimer=null;
var gameOn=false;
var isLocked=true;
var gameX, gameY;
var maxX=19*24;
var maxY=12*24;
var iOffset=6;
var centerX=Math.floor(maxX/2);
var ship=new gameObject();
var iMatrix=new gameObject();
var inv=new Array();
var shot=new Array();
for (var i=0; i<4; i++) shot[i]=new gameObject();
var bomb=new Array();
for (var i=0; i<20; i++) bomb[i]=new gameObject();
var exp=new Array();
for (var i=0; i<4; i++) exp[i]=new gameObject();
var bar=new Array();
for (var i=0; i<4; i++) bar[i]=new gameObject();
var hStep, vStep, sStep, irows, icols, wave, level, score, lifes;
var shots, bombs, bsrad, bfac, bfacMax, sirad;
var iLeft, iRight, iBottom;
var shipdx, idx, invp, invpn, vSpeedLimit, introCounter;

var aSpeed=60;
var baseSpeed=120;
var baseStep=2;

var waveLayout1= new Array([3,4], [3,6], [4,6]);
var waveLayout2= new Array([3,6], [4,6], [4,6]);
var barPosX= new Array(48, 168, 288, 408);
var maxRandom=0.35;

function newGame() {
	if (gameTimer) clearTimeout(gameTimer);
	isLocked=true;
	var ready=setAPI();
	if (ready==false) return;
	hideGameDisplay();
	getBasePos();
	gameOn=true;
	level=0;
	wave=0;
	score=0;
	lifes=3;
	for (var i=1; i<4; i++) setimg('life'+i, 'ship');
	resetScore();
	newShip(true);
	newLevel()
}

function newShip(init) {
	shipdx=0;
	if (init) {
		ship.x=centerX;
		ship.y=maxY-24
	};
	setimg('life'+lifes, 'ship2');
	moveLayer('shipLr', gameX+ship.x, gameY+ship.y);
	setSprite('shipLr','ship','ship2');
	showLayer('shipLr');
	ship.s= (init)? 0 : -14*4;
}

function newLevel() {
	hideLayer('invLr');
	isLocked=true;
	level++;
	showLevel();
	var stage=(level-1)%3;
	var newWave=(stage==0);
	if (newWave) wave++;
	if (wave==1) {
		irows=waveLayout1[stage][0];
		icols=waveLayout1[stage][1];
	}
	else {
		irows=waveLayout2[stage][0];
		icols=waveLayout2[stage][1];
	};
	for (var r=0; r<4; r++) {
		inv[r]=new Array();
		for (var c=0; c<6; c++) {
			inv[r][c]=new gameObject();
			if ((r<irows) && (c<icols)) {
				inv[r][c].s=1;
				inv[r][c].x=32*c+4;
				inv[r][c].y=24*r;
				inv[r][c].isBottom= (r==irows-1);
				setSprite('invLr','inv'+r+c,'inv1');
			}
			else {
				inv[r][c].s= inv[r][c].x= inv[r][c].y=0;
				inv[r][c].isBottom= false;
				setSprite('invLr','inv'+r+c,'bg');
			}
		}
	};
	iMatrix.x=0;
	iMatrix.y=0;
	idx=1;
	invp=1;
	invpn=1;
	setTiming();
	setInvClipping();
	// reset shots
	for (var i=0; i<4; i++) {
		hideShot(i);
		hideExp(i)
	};
	for (var i=0; i<20; i++) hideBomb(i,false);
	for (var i=0; i<4; i++) {
		bar[i].s=3;
		var lr='barLr'+i;
		bar[i].x=barPosX[i];
		bar[i].y=maxY-48;
		moveLayer(lr, gameX+bar[i].x, gameY+bar[i].y);
		setSprite(lr,'bar'+i, 'bar3');
		showLayer(lr);
	};
	shots=0;
	bombs=0;
	// display it
	iMatrix.y-=24*irows;
	moveLayer('invLr', gameX+iOffset, gameY+iMatrix.y-1);
	clipLayer('invLr', 0, 0, 1, 1);
	showLayer('invLr');
	introCounter=2;
	if (level>1) setScore(200);
	if (newWave) gameTimer=setTimeout("waveIntro()",aSpeed)
	else gameTimer=setTimeout("levelIntro()",aSpeed);
}

function setTiming() {
	var stage=(level-1)%3;
	aSpeed=Math.floor(30*baseStep);
	hStep=baseStep+Math.round((wave*baseStep+1)/4);
	if (hStep>24) hStep==24;
	vStep=Math.round(hStep*1.8);
	sStep=hStep+1;
	sirad=14+vStep;
	bsrad=Math.round(vStep/2)+16;
	bfac=(0.007+stage*0.001)*Math.pow(1.5,wave)*baseStep/4;
	bfacMax=maxRandom*baseStep/4;
	if (bfac>bfacMax) bfac=bfacMax;
	vSpeedLimit=maxY-(72*wave);
	if (stage==2) vSpeedLimit-72;
}


// movements

function doMove() {
	if (gameTimer) clearTimeout(gameTimer);
	var dateA=new Date();
	
	doExplosions();
	if (ship.s>0) {
		ship.ss-=baseStep;
		if (ship.ss<=0) {
			ship.ss=4;
			ship.s--;
			if (ship.s<=0) {
				if (lifes>0) newShip(false);
				else {
					setSprite('shipLr','ship','bg');
					setTimeout('gameOver(0)',baseSpeed);
					return
				}
			}
			else setSprite('shipLr','ship','exp1'+ship.s)
		}
	}
	else if (ship.s<0) {
		ship.s+=baseStep;
		if (ship.s>=0) {
			ship.s=0;
			isLocked=false;
			setSprite('shipLr','ship','ship')
		};
		moveShip()
	}
	else moveShip();
	var lastInv=((moveShots()) || (moveInvaders()));
	if (lastInv) {
		isLocked=true;
		gameTimer=setTimeout('endLevel()', baseSpeed);
		return
	};
	moveBombs();
	
	if (gameOn) {
		var dateB=new Date();
		mTO= dateA.getTime()-dateB.getTime();
		mTO+=aSpeed;
		if (mTO<5) mTO=5;
		gameTimer=setTimeout("doMove()",mTO)
	}
}

function endLevel() {
	if (gameTimer) clearTimeout(gameTimer);
	var dateA=new Date();
	
	if ((exp[0].s+exp[1].s+exp[2].s+exp[3].s==0) && (ship.s<=0)) {
		if (ship.s<0) {
			ship.s=0;
			setSprite('shipLr','ship','ship')
		};
		gameTimer=setTimeout('newLevel()', baseSpeed);
		return
	};
	doExplosions();
	if (ship.s>0) {
		ship.ss+=baseStep;
		if (ship.ss<=0) {
			ship.ss=4;
			ship.s--;
			if (ship.s==0) {
				if (lifes>0) newShip(false);
				else {
					setSprite('shipLr','ship','bg');
					setTimeout('gameOver(0)',baseSpeed);
					return
				}
			}
			else setSprite('shipLr','ship','exp1'+ship.s)
		}
	}
	else moveShip();
	moveShots();
	moveBombs();
	
	if (gameOn) {
		var dateB=new Date();
		mTO= dateA.getTime()-dateB.getTime();
		mTO+=aSpeed;
		if (mTO<5) mTO=5;
		gameTimer=setTimeout("endLevel()",mTO)
	}
}

function moveShip() {
	if (shipdx!=0) ship.x+=shipdx*sStep;
	if (ship.x<0) {
		ship.x=0;
		shipdx=0
	}
	else if (ship.x>maxX) {
		ship.x=maxX;
		shipdx=0
	};
	moveLayer('shipLr', gameX+ship.x, gameY+ship.y);
}

function moveInvaders() {
	iMatrix.x+=idx*hStep;
	if (iMatrix.x<iLeft) {
		iMatrix.x=iLeft;
		idx=1;
		iMatrix.y+=(iMatrix.y+iBottom>=vSpeedLimit)? 18:12;
		if (iMatrix.y+iBottom>maxY) { gameOver(1); return }; 
	}
	else if (iMatrix.x>maxX-iRight) {
		iMatrix.x=maxX-iRight;
		idx=-1;
		iMatrix.y+=(iMatrix.y+iBottom>=vSpeedLimit)? 18:12;
		if (iMatrix.y+iBottom>maxY) { gameOver(1); return }; 
	}
	moveLayer('invLr', gameX+iMatrix.x+iOffset, gameY+iMatrix.y);
	
	var img='inv'+invp;
	invp+=invpn;
	if ((invp==1) || (invp==3)) invpn=-invpn;
	
	var iv, px;
	var slimit=ship.y-24;
	for (var r=0; r<irows; r++) {
		for (var c=0; c<icols; c++) {
			iv=inv[r][c];
			if (iv.s) {
				setSprite('invLr','inv'+r+c,img);
				if (iv.isBottom) {
					px=iMatrix.x+iv.x;
					if ((iv.y+iMatrix.y>slimit) && (px>ship.x-20) && (px<ship.x+20)) {
						var lastInv=hideInvader(r,c);
						resetShip();
						if (lastInv) return true;
					}
					else if (Math.random()<bfac) newBomb(px, iMatrix.y+iv.y+4);
				}
			}
		}
	};
	return false
}

function hideInvader(r,c) {
	setSprite('invLr','inv'+r+c,'bg');
	inv[r][c].s=0;
	setScore(25);
	var last=setInvClipping();
	if (last) return true;
	if (inv[r][c].isBottom) {
		for (var k=r-1; k>=0; k--) {
			if (inv[k][c].s) {
				inv[k][c].isBottom=true;
				return false;
			}
		};
		bfac+=bfac/6;
		if (bfac>bfacMax) bfac=bfacMax;
	};
	return false;	
}

function setInvClipping() {
	var ml=10;
	var mr=-1;
	var mt=10;
	var mb=-1;
	for (var r=0; r<irows; r++) {
		var rl=10;
		var rr=-1;
		for (var c=0; c<icols; c++) {
			if (inv[r][c].s) {
				if (c<rl) rl=c;
				if (c>rr) rr=c;
			}
		};
		if (rr>-1) {
			if (rl<ml) ml=rl;
			if (rr>mr) mr=rr;
			if (r<mt) mt=r;
			if (r>mb) mb=r;
		}
	};
	if (mr==-1) {
		hideLayer('invLr');
		return true
	};
	mr++;
	mb++;
	clipLayer('invLr', 32*ml, mt*24, 32*mr, 24*mb);
	iLeft=-1*(iOffset+32*ml);
	iRight=32*mr-2*iOffset;
	iBottom=24*mb;
	return false
}

function levelIntro() {
	if (gameTimer) clearTimeout(gameTimer);
	var dateA=new Date();
	iMatrix.y+=Math.floor(introCounter);
	if (iMatrix.y>0) {
		clipLayer('invLr', 0, 0, 32*(icols+1), 24*(irows+1));
		iMatrix.y=0;
		moveLayer('invLr', gameX+iOffset, gameY+iMatrix.y);
		ship.x=centerX;
		shipdx=0;
		ship.s=0;
		moveLayer('shipLr', gameX+ship.x, gameY+ship.y);
		setSprite('shipLr','ship','ship');
		gameTimer=setTimeout("doMove()",baseSpeed);
		isLocked=false;
		return
	};
	introCounter+=.2;
	moveLayer('invLr', gameX+iOffset, gameY+iMatrix.y)-1;
	clipLayer('invLr', 0, -iMatrix.y, 32*(icols+1), 24*(irows+1));
	
	for (var r=0; r<irows; r++) {
		for (var c=0; c<icols; c++) {
			if (inv[r][c].s) setSprite('invLr','inv'+r+c,'inv'+invp);
		}
	};
	invp+=invpn;
	if ((invp==1) || (invp==3)) invpn=-invpn;
	
	if (ship.x!=centerX) {
		var cx=(ship.x>centerX)? -1:1;
		ship.x+=Math.round((centerX-ship.x)*.15) + cx;
		moveLayer('shipLr', gameX+ship.x, gameY+ship.y);
	}
	
	if (gameOn) {
		var dateB=new Date();
		mTO= dateA.getTime()-dateB.getTime();
		//mTO+=Math.floor(baseSpeed/3);
		mTO+=40;
		if (mTO<5) mTO=5;
		gameTimer=setTimeout("levelIntro()",mTO)
	}
}

function waveIntro() {
	introCounter--;
	if (introCounter) {
		setGameDisplay('Wave '+wave+'<BR><SMALL>Get ready.<SMALL>');
		gameTimer=setTimeout("waveIntro()",1200)
	}
	else {
		hideGameDisplay();
		if (level>1) setScore(100+100*wave);
		introCounter=2;
		gameTimer=setTimeout("levelIntro()",200)
	}
}

function moveShots() {
	var s, iv;
	for (var i=0; i<4; i++) {
		s=shot[i];
		if (s.s) {
			s.y-=vStep;
			if (s.y<0) {
				hideShot(i);
				break
			}
			else moveLayer('shotLr'+i,gameX+s.x,gameY+s.y);
			for (var r=0; r<irows; r++) {
				if (s.s) {
					for (var c=0; c<icols; c++) {
						iv=inv[r][c];
						if ((iv.s)  && (isCollision1( iMatrix.x+iv.x, iMatrix.y+iv.y, s.x, s.y, 15, sirad))) {
							hideShot(i);
							var last=hideInvader(r,c);
							newExplosion(i, iMatrix.x+iv.x, iMatrix.y+iv.y);
							if (last) return true;
							break
						}
					}
				}
				else break;
			}
		}
	};
	return false
}

function moveBombs() {
	var b, br, s;
	var bry=bar[0].y;
	var spy=ship.y;
	var spx=ship.x;
	for (var i=0; i<20; i++) {
		b=bomb[i];
		if (b.s) {
			for (var k=0; k<4; k++) {
				s=shot[k];
				if ((s.s) && (isCollision2(b, s, 8, bsrad))) {
					hideBomb(i,true);
					hideShot(k);
					break
				}
			}
		};
		if (b.s) {
			b.y+=vStep;
			if (b.y>maxY-18) hideBomb(i,false)
			else moveLayer('bombLr'+i,gameX+b.x,gameY+b.y);
		};
		if ((b.s) && (Math.abs(b.y-bry)<vStep)) {
			for (var k=0; k<4; k++) {
				br=bar[k];
				if ((br.s) && (br.x>b.x-15) && (br.x<b.x+15)) {
					br.s--;
					hideBomb(i, false);
					if (br.s) setSprite('barLr'+k, 'bar'+k, 'bar'+br.s)
					else hideLayer('barLr'+k);
					break
				}
			}
		};
		if ((b.s) && (Math.abs(b.y-spy)<vStep)) {
			if ((spx>b.x-15) && (spx<b.x+15)) {
				hideBomb(i, false);
				if (ship.s==0) {
					resetShip();
				}
			}
		}
	}
}

function resetShip() {
	isLocked=true;
	setimg('life'+lifes, 'bg');
	lifes--;
	ship.s=8;
	ship.ss=4;
	setSprite('shipLr','ship', 'exp16');
}

function isCollision1(x1,y1,x2,y2,  rh, rv) {
	return ((Math.abs(x1-x2)<rh) && (Math.abs(y1-y2)<rv))
}

function isCollision2(obj1, obj2, rh, rv) {
	return ((Math.abs(obj1.x-obj2.x)<rh) && (Math.abs(obj1.y-obj2.y)<rv))
}

// shots & bombs

function doShot() {
	if (isLocked) return;
	if (shots<4) {
		for (var i=0; i<4; i++) {
			var b=bar[i];
			if ((b.s) && (b.x>ship.x-14) && (b.x<ship.x+14)) return;
		};
		var i=0;
		while ((i<4) && (shot[i].s!=0)) i++;
		if (i>3) return;
		var s=shot[i];
		s.s=1;
		s.x=ship.x;
		s.y=ship.y;
		var lr='shotLr'+i;
		moveLayer(lr,gameX+s.x,gameY+s.y);
		showLayer(lr);
		shots++
	}
}

function hideShot(i) {
	shot[i].s=0;
	hideLayer('shotLr'+i);
	shots--
}

function newBomb(x,y) {
	if (bombs<20) {
		bombs++;
		var i=0;
		while ((i<20) && (bomb[i].s!=0)) i++;
		if (i>19) return;
		var b=bomb[i];
		b.s=1;
		b.x=x;
		b.y=y;
		var lr='bombLr'+i;
		moveLayer(lr,gameX+x,gameY+y);
		showLayer(lr)
	}
}

function hideBomb(i,doscore) {
	bomb[i].s=0;
	hideLayer('bombLr'+i);
	bombs--;
	if (doscore) setScore(1);
}

function newExplosion(i,x,y) {
	if (exp[i].s) hideLayer('expLr'+i);
	var e=exp[i];
	e.s=5;
	e.x=x;
	e.y=y;
	var lr='expLr'+i;
	moveLayer(lr,gameX+x,gameY+y);
	setSprite(lr,'exp'+i,'exp25');
	showLayer(lr)
}

function doExplosions() {
	var e;
	for (var i=0; i<4; i++) {
		e=exp[i];
		if (e.s) {
			e.s--;
			if (e.s)  setSprite('expLr'+i,'exp'+i,'exp2'+e.s)
			else hideLayer('expLr'+i);
		}
	}
}

function hideExp(i) {
	exp[i].s=0;
	hideLayer('expLr'+i);
}

// global functions

function setGameDisplay(t) {
	writeLayer('gameDisplay1', '<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0" WIDTH="480" HEIGH="60"><TR><TD ALIGN="center" VALIGN="middle" CLASS="gamedisplay1"><I>'+t+'</I></TD></TR></TABLE>');
	writeLayer('gameDisplay2', '<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0" WIDTH="480" HEIGH="60"><TR><TD ALIGN="center" VALIGN="middle" CLASS="gamedisplay2"><I>'+t+'</I></TD></TR></TABLE>');
	clipLayer('gameDisplay1', 0,0,480,60);
	clipLayer('gameDisplay2', 0,0,480,60);
	moveLayer('gameDisplay2',gameX+1,gameY+111);
	moveLayer('gameDisplay1',gameX,gameY+110);
	showLayer('gameDisplay1');
	showLayer('gameDisplay2')
}

function hideGameDisplay() {
	hideLayer('gameDisplay2');
	hideLayer('gameDisplay1')
}

function showLevel() {
	var lStr= (level<10)? ''+level+'n' : ''+(level%100);
	setimg('l1', 'n'+lStr.charAt(0));
	setimg('l2', 'n'+lStr.charAt(1))
}

function setScore(s) {
	score+=s;
	var st=''+score;
	var sl=st.length;
	if (sl>8) {
		st=st.substring(sl-8,sl);
		sl=8
		};
	var os=8-sl;
	for (var n=0; n<sl; n++) {
		setimg("s"+(os+n),"n"+st.charAt(n));
	}
}

function resetScore() {
	score=0;
	var st='00000000';
	var sl=st.length;
	if (sl>8) {
		st=st.substring(sl-8,sl);
		sl=8
		};
	var os=8-sl;
	for (var n=0; n<sl; n++) {
		setimg("s"+(os+n),"n"+st.charAt(n));
	}
}

function gameOver(forced) {
	if (gameOn) {
		isLocked=true;
		gameOn=false;
		if (forced) setGameDisplay('<SMALL>Alien forces broke through.</SMALL><BR>Game over.')
		else setGameDisplay('Game over.');
	}
}

function doPause() {
	if (isLocked) return;
	if (gameOn) {
		if (gameTimer) clearTimeout(gameTimer);
		gameOn=false;
		setGameDisplay('Paused.')
	}
	else {
		gameOn=true;
		gameTimer=setTimeout('doMove()',100);
		hideGameDisplay()
	}
}

function setSmoothness(nStep) {
	setimg('step'+baseStep,'off');
	setimg('step'+nStep,'on');
	baseStep= nStep;
	setTiming()
}