diff options
Diffstat (limited to 'game.js')
-rw-r--r-- | game.js | 243 |
1 files changed, 243 insertions, 0 deletions
@@ -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); |