window.addEventListener("load", () => { "use strict"; var game; var arena = document.getElementById("arena"); var enemies = document.getElementById("enemies"); var bullets = document.getElementById("bullets"); var score = document.getElementById("score"); var music = []; var musicIdx = 0; function flipMusic() { console.log("Playing music track " + musicIdx); music[musicIdx].currentTime = 0; music[musicIdx].play(); musicIdx = 1 - musicIdx; window.setTimeout(flipMusic, 72000); } document.getElementById("title").addEventListener("click", e => { document.getElementById("title").style.setProperty("display", "none"); for (var i = 0; i < 2; i++) { music[i] = new Audio("veggie-baggle.mp3"); } var musicStarted = false; music[0].addEventListener("canplaythrough", () => { if (!musicStarted) { musicStarted = true; flipMusic(); } }); frame(); }); arena.addEventListener("mousemove", e => { if (game) { game.mx = e.clientX; game.my = e.clientY; } }); function onKill() { game.killed++; game.spawns.push(new Enemy()); if (game.killed % 5 == 0) { game.spawns.push(new Enemy()); } if (game.killed % 9 == 0) { game.spawns.push(new Bee()); } } class Actor { constructor(element, container) { this.element = document.createElement(element); container.appendChild(this.element); this.x = 0; this.y = 0; } get content() { return this.element.innerText; } set content(text) { this.element.innerText = text; } get width() { return this.element.clientWidth; } get height() { return this.element.clientHeight; } update(dt) { var style = this.element.style; style.setProperty('left', Math.floor(this.x) + 'px'); style.setProperty('top', Math.floor(this.y) + 'px'); // despawn enemies as soon as they leave the screen if (this.x > arena.clientWidth || this.x + this.width < 0 || this.y > arena.clientHeight || this.y + this.height < 0) { this.die(); return false; } return true; } die() { if (this.element.parentElement) { this.element.parentElement.removeChild(this.element); } } } class HealthBar { constructor(parent, maxVal) { this.element = document.createElement("div"); this.element.setAttribute("class", "healthBar"); parent.appendChild(this.element); this.maxVal = maxVal; this.value = maxVal; } get value() { return this._value; } set value(val) { this._value = val; var level = val*100.0/this.maxVal; this.element.style.setProperty('width', level + '%'); if (level > 50) { this.element.setAttribute("class", "healthBar full"); } else if (level > 25) { this.element.setAttribute("class", "healthBar medium"); } else if (level > 0) { this.element.setAttribute("class", "healthBar low"); } else { this.element.setAttribute("class", "healthBar empty"); } } } class Bullet extends Actor { constructor(x, y, dx, dy, adjust) { super("li", bullets); this.x = x + dx*adjust; this.y = y + dy*adjust; this.dx = dx; this.dy = dy; this.element.addEventListener("mouseover", () => game.health.value--); this.update(0); } update(dt) { this.x += this.dx*dt; this.y += this.dy*dt; return super.update(dt); } } class Enemy extends Actor { constructor() { super("div", enemies); this.content = "🍆"; this.element.setAttribute("class", "alive"); this.size = (Math.random()*3 + 1)*100; this.element.style.setProperty("font-size", this.size + '%') this.healthBar = new HealthBar(this.element, this.size); this.x = Math.random()*(arena.clientWidth - this.width) + this.width/2; this.y = Math.random()*(arena.clientHeight/2 - this.height) + this.height/2; this.hurting = false; this.element.addEventListener("mouseover", () => this.hurting = true); this.element.addEventListener("mouseout", () => this.hurting = false); this.shootAngle = Math.random()*2*Math.PI; this.shootAngleInc = Math.random()*2*Math.PI; this.shootFreq = Math.random()*50 + 20; this.shootVelocity = Math.random()*0.5 + 0.05; this.nextShot = Math.random()*1000 + 100; } update(dt) { this.x += (Math.random() - Math.random())*dt*this.size/1000; this.y += (Math.random() - Math.random())*dt*this.size/1000; if (this.hurting) { game.score += Math.floor(dt*this.size); this.healthBar.value -= dt/10; if (this.healthBar.value <= 0) { game.score += Math.floor(this.healthBar.maxVal*10); this.content = "✨"; this.element.setAttribute("class", "exploding"); this.element.style.setProperty("font-size", (this.size*5) + '%'); this.element.addEventListener("transitionend", () => this.die()); onKill(); return false; } } else { this.nextShot -= dt; while (this.nextShot <= 0) { var dx = this.shootVelocity*Math.sin(this.shootAngle); var dy = this.shootVelocity*Math.cos(this.shootAngle); game.spawns.push(new Bullet( this.x, this.y, dx, dy, this.nextShot)); this.shootAngle += this.shootAngleInc; this.nextShot += this.shootFreq; } } return super.update(dt); } } class Bee extends Actor { constructor() { super("div", enemies); this.content = "🍑"; this.element.setAttribute("class", "alive"); this.size = (Math.random() + 1)*Math.min(arena.clientWidth/4, arena.clientHeight/8); this.element.style.setProperty("font-size", this.size + 'px'); this.healthBar = new HealthBar(this.element, this.size*3); this.x = this.cx = Math.random()*arena.clientWidth/10 + arena.clientWidth/2; this.y = this.cy = Math.random()*arena.clientHeight/10 + arena.clientHeight/4; this.hurting = false; this.element.addEventListener("mouseover", () => this.hurting = true); this.element.addEventListener("mouseout", () => this.hurting = false); this.phase = Math.random()*Math.PI*2; this.freqX = Math.random()*0.001 + 0.001; this.freqY = Math.random()*0.001 + 0.001; this.dx = Math.random()*Math.min(this.x, arena.clientWidth - this.cx) - this.size/3; this.dy = Math.random()*Math.min(this.y, arena.clientHeight - this.cy) - this.size/3; this.shootFreq = Math.random()*30 + 20; this.shootVelocity = Math.random()*0.5 + 0.07; this.nextShot = Math.random()*1000 + 100; } update(dt) { this.phase += dt; this.x = this.cx + Math.sin(this.phase*this.freqX)*this.dx; this.y = this.cy + Math.sin(this.phase*this.freqY)*this.dy; if (this.hurting) { this.x += (Math.random() - Math.random())*20; this.y += (Math.random() - Math.random())*20; game.score += Math.floor(dt*this.size); this.healthBar.value -= dt/10; if (this.healthBar.value <= 0) { game.score += Math.floor(this.healthBar.maxVal*10); this.content = "✨"; this.element.setAttribute("class", "exploding"); this.element.style.setProperty("font-size", (this.size*5) + '%'); this.element.addEventListener("transitionend", () => this.die()); onKill(); return false; } } else { this.nextShot -= dt; while (this.nextShot <= 0) { var ox = game.mx - this.x; var oy = game.my - this.y; var len = Math.sqrt(ox*ox + oy*oy); game.spawns.push(new Bullet( this.x, this.y, ox*this.shootVelocity/len, oy*this.shootVelocity/len, this.nextShot)); this.nextShot += this.shootFreq; } } return super.update(dt); } } game = { score: 0, health: new HealthBar(arena, 1000), spawns: [], actors: [], killed: 0, }; function update(dt) { if (game.health.value <= 0) { return; } game.spawns.forEach(spawn => game.actors.push(spawn)); game.spawns = [] if (game.actors.length < 1) { game.actors.push(new Enemy()); } game.actors = game.actors.filter(actor => actor.update(dt)); score.innerText = game.score; } var lastTime = new Date(); function frame() { var now = new Date(); update(now - lastTime); lastTime = now; (window.requestAnimationFrame || window.setTimeout)(frame, 1000.0/30); } // frame(); });