ANGRY SNEK



LOG | FILES | OVERVIEW



let cursor = {
	'x' : 0,
	'y' : 0
};

let canvas;
let context;

let mice;
let explosions;

const drawInterval=20/1000;

let micePerSecond=0.3;
const chargeDepletionRate=200;

let normalMouseSpeed=3;
let normalMouseSize=1;

const maxNumberOfExplosionParticles=20;
const minNumberOfExplosionParticles=5;
const maxParticleSize=50;
const maxExplosionAge=100;


let lastTimeStamp;

let snake = { 
	'x':window.innerWidth/2,
	'y':window.innerHeight/2,
	'discharging': false,
	'direction' : 0
	
};

let health=10;
let score=0;

let frameId;
let snakeLaserId;

let charge=0;
const chargeTipoffPoint=10;

function draw(timeStamp)
{
	let timeElapsed;

	if(lastTimeStamp==undefined)
		lastTimeStamp=timeStamp;

	timeElapsed=Math.min((timeStamp-lastTimeStamp)/1000,0.1);
	lastTimeStamp=timeStamp;

	canvas.width  = window.innerWidth;
	canvas.height = window.innerHeight;

	/*the only clear thing here is that I am very bad at geometry*/
	context.clearRect(0, 0, canvas.width, canvas.height);


	drawExplosions(timeElapsed);
	drawMice(timeElapsed);
	drawSnake(timeElapsed);
	drawHealth();

	frameId=window.requestAnimationFrame(draw);
}
function drawHealth()
{
	context.save();
	context.font='48px serif';
	context.fillText(`Health: ${health}`,10,70);

	context.font='48px serif';
	context.fillText(`Score: ${score}/30`,window.innerWidth-48*10,70);
	context.restore();
}
function createMouse(x,y,direction,speed,size)
{
	return { 'x':x, 'y':y, 'direction':direction, 'speed':speed,'size':size ,'tailLag':0, 'animation':0, 'animationCounter':0, 'hitbox':size*90 };
}


function main()
{
	initialise();
	frameId=window.requestAnimationFrame(draw);
	setInterval(spawnMouse,1000/micePerSecond);
	setInterval(cleanUpMice,100);
	setInterval(cleanUpBlood,maxExplosionAge);

	setInterval(depleteCharge,chargeDepletionRate);
}

function spawnMouse()
{
	mice.push(createMouse(5+window.innerWidth*Math.random()*0.9,window.innerHeight+normalMouseSize*90,-Math.PI/2,(normalMouseSpeed*(Math.random()+1)),normalMouseSize*(Math.random()+1)));
} 

function initialise()
{

	canvas=document.getElementById("canvas1");
	context=canvas.getContext("2d");

	canvas.addEventListener('mousemove', e => { cursor.x=e.offsetX; cursor.y=e.offsetY; });
	canvas.addEventListener('touchmove', e => 
			{
				cursor.x=e.changedTouches[0].clientX;
				cursor.y=e.changedTouches[0].clientY;
			
			});
	mice=[];
	mice.push(createMouse(200,100,1,normalMouseSpeed,normalMouseSize));

	explosions=[];
}
function cleanUpMice()
{
	let i=0;
	for(i;i<mice.length;++i)
	{
		if(mice[i].y<-200 || mice[i].y>window.innerHeight+300 || mice[i].x<-100 || mice[i].x>window.innerWidth+300)
		{
			mice.splice(i,1);
			--health;
			if(health==0)
			{
				window.cancelAnimationFrame(frameId);
				alert(`Too many of the filthy rats escaped! Your snake has died of despair. Your score is ${score}`);
			}
		}
		else if(mice[i].x-mice[i].hitbox<snake.x && mice[i].x+mice[i].hitbox>snake.x && mice[i].y+mice[i].hitbox>snake.y && mice[i].y-mice[i].hitbox<snake.y)
		{
			explosions.push( makeExplosion(mice.splice(i,1)[0]));
			checkScore();
		}
		else if(snake.discharging && mouseColidesWithLaserBeam(mice[i]))
		{
			explosions.push( makeExplosion(mice.splice(i,1)[0]));
			checkScore();
		}

	}
}
function cleanUpBlood()
{
	let i=0;
	for(i;i<explosions.length;++i)
	{
		if(explosions[i].animation>maxExplosionAge)
			explosions.splice(i,1);
	}
}
function drawSnake(timeElapsed)
{
	let distance;
	let distance2;
	let vx;
	let vy;
	let wx;
	let wy;

	distance2=Math.sqrt(Math.pow(window.innerWidth/2-snake.x,2) + Math.pow(window.innerHeight/2-snake.y,2))+0.1;
	wx=((window.innerWidth/2)-snake.x)/distance2;

	gravitate(snake,cursor,true,10000*(timeElapsed/drawInterval),100);

	distance=Math.sqrt(Math.pow(window.innerWidth/2-snake.x,2) + Math.pow(window.innerHeight/2-snake.y,2))+0.1;
	vx=(window.innerWidth/2-snake.x)/distance;
	vy=(window.innerHeight/2-snake.y)/distance;

	
	if(charge>chargeTipoffPoint && snake.discharging==false)
	{
		snake.discharging=true;
		snakeLaserId=setInterval(cleanUpMice,drawInterval);
	}else if(snake.discharging==true && charge<=0)
	{
		snake.discharging=false;
		clearInterval(snakeLaserId);
	}

	if(snake.discharging==true)
	{
		gravitate(snake,{'x':Math.random()*window.innerWidth,'y':Math.random()*window.innerHeight/4},false,10000*(timeElapsed/drawInterval),1);
		charge+=Math.abs(Math.asin(wx)-Math.asin(vx))*0.5;
	}else
	{
		charge+=Math.abs(Math.asin(wx)-Math.asin(vx));
	}

	context.save();

	context.shadowBlur=charge;
	context.shadowColor="yellow";
	/*body*/
	context.beginPath();
	context.strokeStyle="#00FF00";
	context.moveTo(window.innerWidth/2,-10);
	context.bezierCurveTo(
				window.innerWidth/2,
				window.innerHeight/4,
				snake.x+vx*300,
				snake.y+vy*100,
				snake.x,
				snake.y
			);
	context.lineWidth=40;
	context.stroke();
	context.closePath();

	context.translate(snake.x,snake.y);
	snake.direction=Math.atan2(vy,vx);
	context.rotate(Math.PI/2+snake.direction);
	/*
	if(wx>0)
	{
		snake.direction=Math.acos(wy);
		context.rotate(Math.PI-snake.direction);
	}else
	{
		snake.direction=Math.acos(wy);
		context.rotate(Math.PI+snake.direction);
	}
	*/

	if(snake.discharging==true)
	{
		context.beginPath();
		context.fillStyle="#FF0000";
		context.rect(-15,0,30,10000);
		context.fill();
		context.closePath();
	}

	context.strokeStyle="#00FF00";
	context.beginPath();
	context.fillStyle = "#00FF00";
	context.ellipse(0,50,30,70,0,0,3*Math.PI);
	context.fill();
	context.stroke();
	context.closePath();

	/*right eye*/
	context.beginPath();
	context.fillStyle="#FF0000"
	context.moveTo(20,80);
	context.lineTo(30,80);
	context.lineTo(15,100);
	context.fill();
	context.closePath();

	/*right eye*/
	context.beginPath();
	context.fillStyle="#FF0000"
	context.moveTo(-20,80);
	context.lineTo(-30,80);
	context.lineTo(-15,100);
	context.fill();
	context.closePath();


	context.restore();

}

function drawExplosions(timeElapsed)
{
	let i=0;
	for(i;i<explosions.length;++i)
	{
		drawExplosion(explosions[i],timeElapsed);
	}
}
function drawExplosion(explosion,timeElapsed)
{
	let i=0;
	explosion.animation+=timeElapsed/drawInterval;
	for(i;i<explosion.particles.length;++i)
	{
		explosion.particles[i].x+=(1000/explosion.animation)*0.1*Math.cos(explosion.particles[i].direction);
		explosion.particles[i].y+=(1000/explosion.animation)*0.1*Math.sin(explosion.particles[i].direction);


		context.fillStyle=`rgb(
					255,
					${Math.floor(255*(explosion.animation/(maxExplosionAge+30)))},
					${Math.floor(255*(explosion.animation/(maxExplosionAge+30)))}

				)`;
		context.rect(
				explosion.particles[i].x,
				explosion.particles[i].y,
				maxParticleSize*explosion.particles[i].size,
				maxParticleSize*explosion.particles[i].size,
				);
		context.fill();
	}
}
function drawMice(timeElapsed)
{
	let i=0;
	for(i;i<mice.length;++i)
	{
		drawMouse(mice[i],timeElapsed);
	}
}
function drawMouse(mouse,timeElapsed)
{
	mouse.animationCounter+=drawInterval;
	mouse.x+=mouse.speed*(timeElapsed/drawInterval)*Math.cos(mouse.direction);
	mouse.y+=mouse.speed*(timeElapsed/drawInterval)*Math.sin(mouse.direction);
	mouse.animation+=0.4*(timeElapsed/drawInterval);

	if(score>=0)
	{
		gravitate(mouse,snake,false,(timeElapsed/drawInterval)*500,10);
	}

	context.save();

	context.translate(mouse.x, mouse.y);
	context.rotate(mouse.direction);
	context.scale(mouse.size,mouse.size);

	/*body*/
	context.beginPath();
	context.ellipse(0,0,90,40,0,0,2*Math.PI);
	context.fillStyle = "#8f8f8f";
	context.fill();
	context.stroke();
	context.closePath();
	/*right ear*/
	context.beginPath();
	context.arc(50, 20, 20, 0, Math.PI*1.5, false);
	context.fillStyle = "#8f8f8f";
	context.fill();
	context.stroke();
	context.closePath();
	/*left ear*/
	context.beginPath();
	context.arc(50, -22, 20, 0,- Math.PI*1.5, true);
	context.fillStyle = "#8f8f8f";
	context.fill();
	context.stroke();
	context.closePath();
	/*left eye*/
	context.beginPath();
	context.ellipse(80,7,5,3,-1/2,0,2*Math.PI);
	context.fillStyle = "#000000";
	context.fill();
	context.closePath();
	/*right eye*/
	context.beginPath();
	context.ellipse(80,-7,5,3,1/2,0,2*Math.PI);
	context.fillStyle = "#000000";
	context.fill();
	context.closePath();
	/*right wiskers*/
	context.beginPath();
	context.moveTo(89,0);
	context.lineTo(95,20);
	context.stroke();
	context.closePath();
	context.beginPath();
	context.moveTo(89,0);
	context.lineTo(90,20);
	context.stroke();
	context.closePath();
	/*left wiskers*/
	context.beginPath();
	context.moveTo(89,0);
	context.lineTo(95,-20);
	context.stroke();
	context.closePath();
	context.beginPath();
	context.moveTo(89,0);
	context.lineTo(90,-20);
	context.stroke();
	context.closePath();

	/*tail*/
	context.beginPath();
	context.moveTo(-90,0);
	context.bezierCurveTo(-100,0,-150,Math.cos(mouse.animation)*30,-200,Math.sin(mouse.animation)*10);
	context.lineWidth+=2;
	context.stroke();
	context.closePath();

	context.restore();
}

function gravitate(subject,gravitas,doesItPull,pullStrength,nearDistance)
{
	let distance=Math.sqrt( (subject.x-gravitas.x)*(subject.x-gravitas.x) + (subject.y-gravitas.y)*(subject.y-gravitas.y));

	if(distance==0)
	{
		return ;
	}

	if(doesItPull==false)
	{
		subject.x+=(((subject.x-gravitas.x)*pullStrength)/(distance*distance));
		subject.y+=(((subject.y-gravitas.y)*pullStrength)/(distance*distance));
	}else
	{
		if(distance<=nearDistance)
		{
			subject.x=gravitas.x;
			subject.y=gravitas.y;
		}else
		{
			subject.x-=(((subject.x-gravitas.x)*pullStrength)/(distance*distance));
			subject.y-=(((subject.y-gravitas.y)*pullStrength)/(distance*distance));
		}

	}
}
function makeExplosion(mouse)
{
	let ret={ 'mouse': mouse, 'animation':0, 'particles':[] };
	let numberOfParticles=Math.floor(Math.random()*(maxNumberOfExplosionParticles-minNumberOfExplosionParticles))+minNumberOfExplosionParticles;
	let i=0;

	++score;
	for(i;i<numberOfParticles;++i)
	{
		ret.particles.push({ 'x':mouse.x , 'y':mouse.y, 'direction':Math.random()*2*Math.PI, 'size':Math.random() });
	}

	return ret;
}
function depleteCharge()
{
	charge=Math.max(0,charge-1);
}
function possiblyDischarge()
{

}
function mouseColidesWithLaserBeam(mouse)
{
	let vx=(mouse.x-snake.x);
	let vy=(mouse.y-snake.y);
	let direction=Math.atan2(vy,vx);

	if(direction<0)
		direction=Math.PI*2+direction;

	if(Math.abs(direction-Math.PI-snake.direction)<0.3)
		return true;
	else
		return false;
}
function checkScore()
{
	if(score==20 || score==10 || score==5)
	{
		normalMouseSpeed*=2;
		normalMouseSize*=0.7;
		setInterval(spawnMouse,1000/micePerSecond);
	}

	if(score>=30)
	{
		window.cancelAnimationFrame(frameId);
		alert("You are winner!");
	}
}
main();