aboutsummaryrefslogtreecommitdiff
path: root/game.js
diff options
context:
space:
mode:
Diffstat (limited to 'game.js')
-rw-r--r--game.js243
1 files changed, 243 insertions, 0 deletions
diff --git a/game.js b/game.js
new file mode 100644
index 0000000..4be774a
--- /dev/null
+++ b/game.js
@@ -0,0 +1,243 @@
+"use strict";
+
+var boardRatio = 1.5;
+var ballRadius = 0.01, padWidth = 0.01, padHeight = 0.15, padX = 0.01;
+var padBaseSpeed = 0.8;
+
+var gameId;
+var cvs, ctx, cvsW, cvsH, bdW, bdH, bdltX, bdltY;
+var socket;
+var playing = false, flip = false;
+var ballX, ballY, ballVX, ballVY;
+var padPos, padVel, pad2Pos, pad2Vel;
+
+function redraw() {
+ ctx.fillStyle = "#000000";
+ ctx.fillRect(0, 0, cvsW, cvsH);
+
+ redrawFast();
+}
+
+function calcBallX() {return bdltX + bdW * (flip ? 1 - ballX : ballX);}
+function calcBallY() {return bdltY + bdH * ballY;}
+function calcPadY() {return bdltY + bdH * ((flip ? pad2Pos : padPos) - padHeight / 2);}
+function calcPad2Y() {return bdltY + bdH * ((flip ? padPos : pad2Pos) - padHeight / 2);}
+
+function redrawFast() {
+ ctx.strokeStyle = "#ffffff";
+ ctx.lineWidth = 2;
+ ctx.strokeRect(bdltX - 1, bdltY - 1, bdW + 2, bdH + 2);
+
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(bdltX + bdW / 2 | 0, bdltY);
+ ctx.lineTo(bdltX + bdW / 2 | 0, bdltY + bdH);
+ ctx.stroke();
+
+ if (playing) {
+ ctx.fillStyle = "#ffffff";
+ ctx.beginPath();
+ ctx.arc(calcBallX(), calcBallY(), ballRadius * bdW, 0, 2 * Math.PI);
+ ctx.rect(bdltX + bdW * padX, calcPadY(), bdW * padWidth, bdH * padHeight);
+ ctx.rect(bdltX + bdW * (1 - padX - padWidth), calcPad2Y(), bdW * padWidth, bdH * padHeight);
+ ctx.fill();
+ }
+}
+
+function undrawElements() {
+ ctx.fillStyle = "#000000";
+ ctx.beginPath();
+ ctx.rect(
+ calcBallX() - ballRadius * bdW - 2, calcBallY() - ballRadius * bdW - 2,
+ 2 * ballRadius * bdW + 4, 2 * ballRadius * bdW + 4);
+ ctx.rect(
+ bdltX + bdW * padX - 2, calcPadY() - 2,
+ bdW * padWidth + 4, bdH * padHeight + 4);
+ ctx.rect(
+ bdltX + bdW * (1 - padX - padWidth) - 2, calcPad2Y() - 2,
+ bdW * padWidth + 4, bdH * padHeight + 4);
+ ctx.fill();
+}
+
+function resizeHandler() {
+ var rect = cvs.getBoundingClientRect();
+ cvsW = cvs.width = rect.width;
+ cvsH = cvs.height = rect.height;
+ // I believe this stuff is correct.
+ if (cvsH * boardRatio > cvsW) {
+ bdW = cvsW;
+ bdH = bdW / boardRatio | 0;
+ bdltX = 2;
+ bdltY = (cvsH - bdH) / 2 + 2 | 0;
+ bdW -= 4; bdH -= 4;
+ } else {
+ bdH = cvsH;
+ bdW = boardRatio * bdH | 0;
+ bdltX = (cvsW - bdW) / 2 + 2 | 0;
+ bdltY = 2;
+ bdW -= 4; bdH -= 4;
+ }
+
+ redraw();
+}
+
+function displayStatus(msg) {
+ document.getElementById("status").innerHTML = msg;
+}
+
+function openGame() {
+ socket = io();
+ socket.on("redirect", function(url) {
+ location.href = url;
+ });
+
+ socket.on("status", function(msg) {
+ displayStatus(msg);
+ });
+
+ socket.on("serve", function(side, x, y) {
+ playing = true;
+ flip = side == "right";
+ ballX = x; ballY = y;
+ padPos = pad2Pos = 0.5;
+ redraw();
+ });
+
+ socket.on("stop", function() {
+ playing = false;
+ stopPhysicsLoop();
+ stopGraphicsLoop();
+ redraw();
+ });
+
+ socket.on("startBall", function() {
+ initPhysics();
+ ballVX = -0.5 / boardRatio;
+ ballVY = -0.5;
+ startPhysicsLoop();
+ startGraphicsLoop();
+ });
+
+ socket.on("start", function() {
+ initPhysics();
+ ballVX = 0.5 / boardRatio;
+ ballVY = -0.5;
+ startPhysicsLoop();
+ startGraphicsLoop();
+ });
+
+ socket.on("padvec", function(pos, vel) {
+ pad2Pos = pos;
+ pad2Vel = vel;
+ });
+
+ socket.emit("open", gameId);
+}
+
+var graphicsLoopId = null, physicsLoopId = null;
+
+function startGraphicsLoop() {
+ // undrawElements();
+ redraw(); // TODO: redrawFast
+ graphicsLoopId = requestAnimationFrame(startGraphicsLoop);
+}
+
+function stopGraphicsLoop() {
+ if (graphicsLoopId == null) return;
+ cancelAnimationFrame(graphicsLoopId);
+ graphicsLoopId = null;
+}
+
+function startPhysicsLoop() {
+ var interval = 1 / 120;
+ physicsLoopId = setInterval(function() {
+ advancePhysics(interval);
+ }, interval * 1000);
+}
+
+function stopPhysicsLoop() {
+ if (physicsLoopId == null) return;
+ clearInterval(physicsLoopId);
+ physicsLoopId = null;
+}
+
+function initPhysics() {
+ ballX = ballY = 0.5;
+ ballVX = ballVY = 0;
+ padPos = pad2Pos = 0.5;
+ padVel = pad2Vel = 0;
+}
+
+function advancePhysics(deltaT) {
+ var newballX = ballX + ballVX * deltaT;
+ var newballY = ballY + ballVY * deltaT;
+ var newpadPos = padPos + padVel * deltaT;
+ var newpad2Pos = pad2Pos + pad2Vel * deltaT;
+
+ var t;
+ if (newballY <= ballRadius) {
+ t = (ballRadius - ballY) / ballVY;
+ } else if (newballY >= 1 - ballRadius) {
+ t = (1 - ballRadius - ballY) / ballVY;
+ } else {
+ t = -1;
+ }
+ if (t >= 0) {
+ newballY = ballY + ballVY * (2 * t - deltaT);
+ ballVY = -ballVY;
+ }
+
+ if (newballX <= padX + padWidth + ballRadius &&
+ Math.abs(newballY - padPos) < padHeight / 2 + ballRadius) {
+ t = (padX + padWidth + ballRadius - ballX) / ballVX;
+ } else if (newballX >= 1 - (padX + padWidth + ballRadius) &&
+ Math.abs(newballY - pad2Pos) < padHeight / 2 + ballRadius) {
+ t = (1 - (padX + padWidth + ballRadius) - ballX) / ballVX;
+ } else {
+ t = -1;
+ }
+ if (t >= 0) {
+ newballX = ballX + ballVX * (2 * t - deltaT);
+ ballVX = -ballVX;
+ }
+
+ ballX = newballX;
+ ballY = newballY;
+ padPos = newpadPos;
+ pad2Pos = newpad2Pos;
+}
+
+function setupBindings() {
+ window.addEventListener("keydown", function(ev) {
+ if (ev.key == "w" || ev.key == "ArrowUp") {
+ padVel = -padBaseSpeed;
+ socket.emit("padvec", padPos, padVel);
+ } else if (ev.key == "s" || ev.key == "ArrowDown") {
+ padVel = padBaseSpeed;
+ socket.emit("padvec", padPos, padVel);
+ }
+ });
+
+ window.addEventListener("keyup", function(ev) {
+ if (ev.key == "w" || ev.key == "ArrowUp" ||
+ ev.key == "s" || ev.key == "ArrowDown") {
+ padVel = 0;
+ socket.emit("padvec", padPos, padVel);
+ }
+ });
+}
+
+window.addEventListener("load", function() {
+ gameId = document.location.href.replace(/.*\//, "");
+
+ cvs = document.getElementById("cvs");
+ ctx = cvs.getContext("2d");
+
+ resizeHandler();
+
+ setupBindings();
+
+ openGame();
+});
+
+window.addEventListener("resize", resizeHandler);