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;iwindow.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].hitboxsnake.x && mice[i].y+mice[i].hitbox>snake.y && mice[i].y-mice[i].hitboxmaxExplosionAge) 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=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=30) { window.cancelAnimationFrame(frameId); alert("You are winner!"); } } main();