Mozzie Mayhem

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
<!doctype html>
<meta charset='utf-8'>
<meta name="viewport" content="width=520, user-scalable=no">
<title>🦟 Mozzie Mayhem</title>
<div id='info'>Move with arrows, shoot with WASD. Don't die. Shoot to start</div>
<canvas id='canvas' style='border:1px outset;background:radial-gradient(#889944 80%, green 90%, black);' 
	width=500 height=500></canvas>
<div id='buttons' style="width:500px;border:1px outset;touch-action:none;user-select:none;"></div>
<style> button{width:60px;height:60px;user-select:none;-webkit-user-select:none;}</style>
<script>
	var drawing = canvas.getContext("2d");
	Object.assign(drawing, {textAlign: "center", textBaseline: "middle", font: "40px serif"});
	var player = {faces:"up", health:100, score:0, x:400, y:400, speed_x:0, speed_y:0, radius:20};
	var items=[player], keys = {}, frame = {};
	var acceleration_per_second = 800, friction = 2;
	onkeydown=(e)=>{
		keys[e.key.length == 1 ? e.key.toLowerCase() : e.key] = true; 
		if (e.key.startsWith("Arrow")) e.preventDefault();
	}
	onkeyup=(e)=>{ keys[e.key.length == 1 ? e.key.toLowerCase() : e.key] = false; }
	["w",["↑","Up"],"a","s","d",["←","Left"],["↓","Down"],["→","Right"]].forEach((key,index)=>{
		var button = buttons.appendChild(document.createElement("button"));
		button.textContent = key[0].toUpperCase();
		var event_key = key[1] ? "Arrow"+key[1] : key[0];
		button.addEventListener("pointerdown", (e) => {keys[event_key]=true} );
		button.onpointerup = button.onpointerleave=() => {keys[event_key]=false};
		button.style.margin = (index < 2) ? "5px 90px" : "3px 10px";
	});
	function circle(x, y, radius, colour) {
		drawing.fillStyle = colour;
		drawing.beginPath();
		drawing.arc(x, y, radius, 0, 2*Math.PI);
		drawing.fill();
	}
	function shoot(direction, speed_x, speed_y, now=performance.now()) {
		player.faces = direction;
		var bullet = {...player, radius: 8, colour:"#ffcc00", created: now, type: "bullet"};
		bullet.x += (direction == "down") ? -22 : (direction == "up") ? 22 : 0;
		bullet.y += (direction == "right") ? 22 : (direction == "left") ? -22 : 0;
		if ((items[0].created|0) < (now - 500)) items.unshift({...bullet, speed_x, speed_y});
		if (!frame.started) frame.started = true;
	};
	function collides(a,b){ return Math.hypot(a.x-b.x, a.y-b.y) < a.radius + b.radius; }
	function player_circle(x,y,radius,colour){circle(player.x +x, player.y +y, radius, colour);}
	function draw_player(){
		var circles = [ [12, 10, 6, "#662266"], [12, -10, 6, "#662266"], // feet
				[4, 20, 8, "#880000"], [4, -20, 8, "#880000"], // hands
				[0, 15, 10, "#225588"], [0	, -15, 10, "#225588"], // shoulders
				[0,0,15,"#000044"], [14,0, 3, "#000044"]]; // head
		if (player.faces == "left") circles.forEach(c=>c[0] *= -1);
		else if (player.faces == "down") circles.forEach(c=>{ [c[0], c[1]] = [c[1], c[0]]; });
		else if (player.faces == "up") circles.forEach(c=>{ [c[0], c[1]] = [c[1], -c[0]]; });
		circles.forEach(c=>player_circle(...c));
	}
	var bug = {symbol:"🦟", x: -50, damage: 18, y: 0, speed_x: 0, speed_y: 0, radius: 20};
	function add_bug(){ items.push({...bug, y: 800 - Math.random() * 1100, count:++bugs});}
	var bugs = 0;
	function in_range(item){return Math.hypot(item.x - player.x, item.y - player.y) < 1000}
	(function do_frame(now=performance.now()) {
		var interval = frame.started ? (now - frame.last_time)/1000 : 0;
		frame.last_time = now;
		if (!bugs || (frame.started && Math.random() < interval && bugs < 100)) add_bug();
		var acceleration = acceleration_per_second * interval;
		player.speed_x += keys.ArrowRight ? acceleration : keys.ArrowLeft ? -acceleration : 0;
		player.speed_y += keys.ArrowDown ? acceleration : keys.ArrowUp ? -acceleration : 0;
		if (keys.w) shoot("up", player.speed_x, player.speed_y -800);
		if (keys.a) shoot("left", player.speed_x -800, player.speed_y);
		if (keys.s) shoot("down", player.speed_x, player.speed_y + 800);
		if (keys.d) shoot("right", player.speed_x + 800, player.speed_y);
		drawing.clearRect(0,0,canvas.width, canvas.height);
		draw_player();
		items.forEach(i => {
			if (i.symbol == "🦟") i.speed_x += (i.x < player.x ? 500*interval : -500*interval);
			if (i.symbol == "🦟") i.speed_y += (i.y < player.y ? 500*interval : -500*interval);
			i.x += i.speed_x * interval;
			i.y += i.speed_y * interval;
			if (i.type !== "bullet") i.speed_x -= i.speed_x * friction * interval;
			if (i.type !== "bullet") i.speed_y -= i.speed_y * friction * interval;
			items.filter(i2=>(i !== i2 && !i2.deleted && collides(i, i2))).forEach(i2=>{
				if (i == player || i2 == player)
					player.health -= ((i.damage|0+i2.damage|0)*interval);
				else if ((i.damage && i2.type=="bullet") || (i2.damage && i.type=="bullet"))
					i.deleted = i2.deleted = (player.score += 1);
			});
			if (i.symbol && !i.deleted) drawing.fillText(i.symbol, i.x, i.y);
			else if (i !== player) circle(i.x, i.y, i.radius, i.colour || "red");
		});
		items = items.filter(item=>( (in_range(item) || item.symbol == "🦟") && !item.deleted) );
		if (player.x > canvas.width && player.speed_x > 0) player.speed_x *= -0.5;
		if (player.x < 0 && player.speed_x < 0) player.speed_x *= -0.5;
		if (player.y > canvas.height && player.speed_y > 0) player.speed_y *= -0.5;
		if (player.y < 0 && player.speed_y < 0) player.speed_y *= -0.5;
		if (player.score < 100 && player.health > 0) requestAnimationFrame(do_frame);
		if (!frame.started) return;
		if (player.score >= 100) info.style.color = "green";
		else info.style.color = (player.health < player.previous_health) ? "red" : "black";
		player.previous_health = player.health = Math.max(0, player.health);
		info.textContent = "Health: "+Math.ceil(player.health)+", Score: "+player.score + "/100";
	})();
</script>