/server/coord.js~
/server/endpoints.js~
/server/constants.js~
+/web/field.js~
+/web/endpoints.js~
+/web/coord.js~
+/web/draw.js~
+/web/main.js~
+/web/play.html~
// dead don't need changing, all info is necessary
var newballs = this.balls.map(b => b.reduce());
var newpads = this.paddles.map(p => p.reduce());
- var theobject = {dead: this.dead, paddles: newpads, balls: newballs};
+ var thedead = this.dead;
+ var totnum = this.numPlayers;
+ var theobject = {n: totnum, dead: thedead, paddles: newpads, balls: newballs};
return theobject;
}
--- /dev/null
+
+
+var c = {
+ // matchmaker
+ WS_PORT: 6789,
+ NUM_PLAYERS: 3,
+ MS_PER_FRAME: 500,
+ WAIT_TIME: 60000, // 1 minute
+ MAX_GAMES: 5, // the most games allowed to go on at once, to be tweaked as needed for purposes
+ // gamestate
+ DYING_TIME_IN_FRAMES: 100,
+ BOARD_RADIUS: 10,
+ OOB_THRESH: 1, // out-of-bounds threshold
+ ANGLE_THRESH: 0.2, //radians, needs to acct for various rotatings going on... can prolly wing it
+ PADDLE_MVT_BONUS: 0.1, // why this value? who knows. the extra speed from paddles in motion
+ // ball
+ MIN_INIT: 3,
+ MAX_INIT: 5, // initial speed constraints
+ BALL_RADIUS: 0.5,
+ MAX_SPEED: 15,
+ SPEED_BUMP: 0.2,
+ // field
+ BOARD_RADIUS: 10, // completely arbitrary actually...
+ // paddle
+ DPADDLE: 0.1,
+ WIDTH_RATIO: 0.1, // paddle is 1/10th of gap rn
+}
+
+
--- /dev/null
+
+
+
+
+
+var Coord = function(x,y) {
+ this.x = x;
+ this.y = y;
+}
+
+
+Coord.prototype.dist2 = function(c2) {
+ // returns square of the distance
+ var dx = this.x-c2.x;
+ var dy = this.y-c2.y;
+ return dx*dx + dy*dy;
+}
+
+Coord.prototype.rotate = function(th) {
+ // rotates about angle, returns new value
+ var new_x = this.x*Math.cos(th) - this.y*Math.sin(th);
+ var new_y = this.x*Math.sin(th) + this.y*Math.cos(th);
+ this.x = new_x;
+ this.y = new_y;
+}
--- /dev/null
+
+
+// contains the routines needed to actually get the stuff drawn
+
+dwallcolor = 'rgb(200,0,0)';
+wallcolor = 'rgb(100,100,0)';
+pcolor = 'rgb(0,0,200)';
+bcolor = 'rgb(100,100,100)';
+
+
+// main function, given everything from the game state, draws on the given context
+var draw = function(state, ctx) {
+ clearCanvas(ctx);
+ // okay, gotta get the endpoints, then draw the walls,
+ // then draw the balls,
+ // then finally the hard part is the paddles
+ var theEndpoints = genEndpoints(state.n, state.dead);
+ var negatives = endpointNegatives(theEndpoints);
+
+ // dead walls
+ for(var ep of theEndpoints) {
+ var deadOption = state.dead.find(d => (d.id==ep.id));
+ if((deadOption !== undefined) && (deadOption.time > 0)) {
+ drawLine(ctx, dwallcolor, ep.f, ep.s);
+ }
+ }
+ // walls
+ for(var ep of negatives) {
+ drawLine(ctx, wallcolor, ep.f, ep.s);
+ }
+ // balls
+ for(var b of state.balls) {
+ drawBall(ctx, bcolor, b);
+ }
+ // finally the paddles...
+ for(var ep of endpoints) {
+ var paddleOption = state.paddles.find(p => (p.id == ep.id));
+ if(paddleOption !== undefined) {
+ // should probably make sure it's not dead?
+ if(!state.dead.some(d => (d.id == ep.id))) {
+ var pps = getPaddlePoints(paddleOption, ep);
+ drawLine(ctx, pcolor, pps.f, pps.s);
+ }
+ }
+ }
+}
+
+var getPaddlePoints = function(paddle, enclosing) {
+ // returns an endpoints object for the paddle
+ // given the desired width of said paddle and the enclosing endpoints
+ var encl_len = dist(enclosing.f, enclosing.s);
+ var pspace_len = encl_len - (2*c.WIDTH_RATIO*encl_len);
+ var place_on_pspace = pspace_len*(paddle.position+1)/2;
+ var above_f = pspace_len + c.WIDTH_RATIO; // distance above the first enclosing point (takes advantage of the increasing in angle thing to determine orientations)
+ var overall_proportion = above_f/encl_len;
+ var vector = [enclosing.s.x-enclosing.f.x, enclosing.s.y-enclosing.f.y];
+ var d = overall_proportion-c.WIDTH_RATIO;
+ var first = new Coord(vector[0]*d, vector[1]*d);
+ d = overall_proportion+c.WIDTH_RATIO;
+ var second = new Coord(vector[0]*d, vector[1]*d);
+ return new Endpoints(first, second, paddle.id);
+}
+
+
+
+var drawBall(ctx, color, c) {
+ ctx.save();
+ ctx.fillStyle = bcolor;
+ ctx.beginPath();
+ ctx.arc(c.x, c.y, BALL_RADIUS, 0, 2*Math.PI, false);
+ ctx.fill();
+ ctx.restore();
+}
+
+var drawLine(ctx, color, c1, c2) {
+ // draws a line from c1 to c2 in color
+ ctx.save();
+ ctx.strokeStyle = color;
+ ctx.beginPath();
+ ctx.moveTo(c1.x, c1.y);
+ ctx.lineTo(c2.x, c2.y);
+ ctx.closePath();
+ ctx.stroke();
+ ctx.restore();
+}
+
+var clearCanvas = function(ctx) {
+ ctx.save();
+ ctx.transform(1,0,0,1,0,0);
+ ctx.clearRect(0,0,cxt.canvas.width, ctx.canvas.height);
+ ctx.restore();
+}
+++ /dev/null
-
-
-function drawngon(n,r) {
- var cvs = document.getElementById('game');
- var ctx = cvs.getContext('2d');
- var angleper = 2*Math.PI/n;
- var center = {x:cvs.width/2, y:cvs.height/2};
-
- ctx.beginPath();
- ctx.moveTo(center.x+r, center.y);
- var start = {x: center.x+r, y:center.y};
- for(var i=1; i<n; i++) {
- var next = rotate(start, center, angleper*i);
- ctx.lineTo(next.x, next.y);
- }
- ctx.closePath();
- ctx.stroke();
-}
-
-
-function rotate(p,c,t) {
- // rotates p around c by t
- var d = {x:p.x-c.x, y:p.y-c.y};
- var r = {x:d.x*Math.cos(t)-d.y*Math.sin(t), y:d.x*Math.sin(t)+d.y*Math.cos(t)};
- var f = {x:r.x+c.x, y:r.y+c.y};
- return f;
-}
--- /dev/null
+
+
+var Endpoints = function(f,s,id) {
+ // f,s are first, second coordinates, in increasing angle
+ // id corresponds to a player, or -1 or smth if n/a
+ // these endpoints enclose a paddle
+ this.f = f;
+ this.s = s;
+ this.id = id;
+}
+
+
--- /dev/null
+
+// for generating/using the actual playing field of the game, I guess it'll be newly generated on each frame? the math is simple enough...
+
+
+
+var genEndpoints = function(n ,dead) {
+ /**
+ /* takes the number of players total (that the game started with, should be NUM_PLAYERS),
+ /* and the array of the dead and dying
+ /* does id's in a bad but expectable way
+ */
+ // so i'll start from a radius out a zero radians
+ var endpoints = [];
+ var players_length = n - dead.length;
+ for(var d of dead) {
+ players_length += d.time/c.DYING_TIME_IN_FRAMES;
+ }
+ var theta = 0;
+ var dtheta = players_length/(2*Math.PI);
+ var coord = new Coord(c.BOARD_RADIUS,0);
+ for(var i=0; i<n; i++) {
+ var deadStatus = dead.find(e=>(e.id==i));
+ if((deadStatus !== undefined) && (deadStatus.time > 0)) {
+ var r = c.BOARD_RADIUS;
+ theta += (deadStatus.time/c.DYING_TIME_IN_FRAMES)/2;
+ var pt1 = new Coord(r*Math.cos(theta), r*Math.sin(theta));
+ theta += (deadStatus.time/c.DYING_TIME_IN_FRAMES)/2;
+ var pt2 = new Coord(r*Math.cos(theta), r*Math.sin(theta));
+ endpoints.push(new Endpoints(pt1, pt2, n));
+ }
+ else {
+ theta += dtheta/2;
+ var pt1 = new Coord(r*Math.cos(theta), r*Math.sin(theta));
+ theta += dtheta/2;
+ var pt2 = new Coord(r*Math.cos(theta), r*Math.sin(theta));
+ endpoints.push(new Endpoints(pt1, pt2, n));
+ }
+ }
+ return endpoints;
+}
+
+var endpointNegatives = function(endpoints) {
+ // generates the opposite of genEndpoints
+ // i.e. genEndpoints gives the spaces where the paddles live,
+ // and this gives the endpoints enclosing walls
+ var newpoints = [];
+ newpoints.push(new Endpoints(endpoints[endpoints.length-1].s, endpoints[0].f, -1));
+ for(var i=1;i<endpoints.length;i++) {
+ newpoints.push(new Endpoints(endpoints[i-1].s, endpoints[i].f, -1));
+ }
+ return newpoints;
+}
+
+var AnglePair = function(f,s,id) {
+ this.f = f;
+ this.s = s;
+ this.id = id;
+}
+var angles = function(n, dead, thresh) {
+ // gives angle pairs for the thresholds and whatnot.
+ var angs = [];
+ var players_length = n - dead.length;
+ for(var d of dead) {
+ players_length += d.time/c.DYING_TIME_IN_FRAMES;
+ }
+ var theta = 0;
+ var dtheta = players_length/(2*Math.PI);
+ var coord = new Coord(c.BOARD_RADIUS,0);
+ for(var i=0; i<n; i++) {
+ var deadStatus = dead.find(e=>(e.id==i));
+ if((deadStatus !== undefined) && (deadStatus.time > 0)) {
+ var r = c.BOARD_RADIUS;
+ theta += (deadStatus.time/c.DYING_TIME_IN_FRAMES)/2;
+ var t1 = theta;
+ theta += (deadStatus.time/c.DYING_TIME_IN_FRAMES)/2;
+ var t2 = theta
+ angs.push(new AnglePair(t1, t2, n));
+ }
+ else {
+ theta += dtheta/2;
+ var t1 = theta;
+ theta += dtheta/2;
+ var t2 = theta;
+ angs.push(new AnglePair(t1, t2, n));
+ }
+ }
+ return angs;
+}
+
+module.exports = {angles: angles, genEndpoints: genEndpoints, endpointNegatives: endpointNegatives};
--- /dev/null
+
+var prefixurl = "ws://localhost:6789";
+
+theSocket = null;
+
+
+var main = function() { // starts everything, gets us going, setup ish
+ var canvas = document.getElementById('c');
+ if(!canvas.getContext) {
+ alert("Sorry, this game requires support for the <canvas> element");
+ return;
+ }
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ ctx = canvas.getContext('2d');
+ // change the 1's to zoom in i think.. todo
+ ctx.transform(1, 0, 0, 1, ctx.width/2, ctx.height/2);
+
+
+ theSocket = new WebSocket(prefixurl);
+ theSocket.onmessage = function(e) {
+ draw(JSON.parse(e.data), ctx);
+ }
+}
+
+
+window.addEventListener("DOMContentLoaded", e => main());
--- /dev/null
+<!doctype html>
+<html>
+ <head>
+ <title>Pong Battle Royale</title>
+ <script type="text/javascript" src="main.js"></script>
+ <script type="text/javascript" src="draw.js"></script>
+ <script type="text/javascript" src="coord.js"></script>
+ <script type="text/javascript" src="constants.js"></script>
+ <style type="text/css">
+ body { background-color: black; }
+ #c {
+ width: 100%;
+ height: 100%;
+ border: 1px solid red;
+ }
+ </style>
+ </head>
+ <body>
+ <canvas id="c">Sorry, you need to have Javascript enabled for this!</canvas>
+ </body>
+</html>