Join screen UI.

master
Ben Burlingham 5 years ago
parent 82f9604043
commit 9afd3d8364
  1. 81
      client/connection.js
  2. 61
      client/content.js
  3. 233
      client/controls.js
  4. 52
      client/join.js
  5. 3
      client/settings.js
  6. 53
      content.css
  7. 105
      controls.css
  8. 166
      index.css
  9. 117
      index.html
  10. 102
      join.css
  11. 8
      notes.txt
  12. 67
      server.js
  13. 2
      server/game.js

@ -0,0 +1,81 @@
//===== Constructor
const Connection = function() {
// Local event listeners
document.addEventListener('L-robots', () => {
this.ws.send(JSON.stringify({ head: { type: 'reposition-robots' }}));
});
document.addEventListener('L-walls', () => {
this.ws.send(JSON.stringify({ head: { type: 'regenerate-walls' }}));
});
document.addEventListener('L-guess', (evt) => {
this.ws.send(JSON.stringify({ head: { type: 'guess' }, rawBody: evt.detail }));
});
document.addEventListener('L-join', (evt) => {
this.connect();
});
};
Connection.prototype.connect = function(){
const names = ["Biff", "Morty", "Herb", "Chester", "Lyle", "Cap", "Dale", "Ned", "Mindy"]
const r = Math.floor(Math.random() * names.length);
const rawInput = names[r] //prompt("What is your name?");
this.ws = new WebSocket('ws://localhost:8080/ricochet?name=' + rawInput);
this.ws.addEventListener('open', this.onOpen.bind(this));
this.ws.addEventListener('error', this.onError.bind(this));
this.ws.addEventListener('message', this.onReceiveMessage.bind(this));
};
//===== Connection event handlers
Connection.prototype.onOpen = function() {
const evt = new Event('L-conn-open');
document.dispatchEvent(evt);
};
Connection.prototype.onError = function(err) {
console.error(err);
const evt = new CustomEvent('L-conn-error', { detail: err });
document.dispatchEvent(evt);
};
Connection.prototype.onReceiveMessage = function({ data }) {
const msg = JSON.parse(data);
console.warn(msg);
if (!msg.type) {
console.warn("Unprocessable message: ", msg)
return;
}
let eventName;
switch (msg.type) {
case 'players':
eventName = 'G-players';
break;
case 'robots':
eventName = 'G-robots';
break;
case 'walls':
eventName = 'G-walls';
break;
case 'guess':
eventName = 'G-guess';
break;
}
if (eventName) {
const evt = new CustomEvent(eventName, { detail: msg });
document.dispatchEvent(evt);
}
};

@ -1,4 +1,6 @@
const Board = function({ parent, squares }) { //===== Constructor
const Content = function({ parent, squares }) {
this.parent = parent; this.parent = parent;
this.squares = squares; this.squares = squares;
@ -7,24 +9,26 @@ const Board = function({ parent, squares }) {
this.listeners = {}; this.listeners = {};
this.initialRobotState = []; this.initialRobotState = [];
this.drawSquares(); // this.drawSquares();
// document.addEventListener('L-conn-open', this.msgStartGame.bind(this));
document.addEventListener('board-reset', this.onReset.bind(this)); // document.addEventListener('board-reset', this.onReset.bind(this));
// from connection: board.drawRobots(data.body);
// from connection: board.drawWalls(data.body);
// from connection: board.onReceiveGuess(data.body);
}; };
Board.prototype.drawRobots = function(robots) { //===== Event handlers
//===== Event handlers
Content.prototype.drawRobots = function(robots) {
this.initialRobotState = robots; this.initialRobotState = robots;
this.robots = {}; this.robots = {};
while (this.parent.getElementsByClassName('content-robot').length) { this.parent.querySelectorAll('.content-robot').forEach(el => el.parentNode.removeChild(el));
const child = this.parent.getElementsByClassName('content-robot')[0]; this.parent.querySelectorAll('.content-arrows').forEach(el => el.parentNode.removeChild(el));
child.parentNode.removeChild(child);
}
while (this.parent.getElementsByClassName('content-arrows').length) {
const child = this.parent.getElementsByClassName('content-arrows')[0];
child.parentNode.removeChild(child);
}
robots.forEach(({ color, i, j }) => { robots.forEach(({ color, i, j }) => {
const { x, y } = this.squares.ijToXy({ i, j }); const { x, y } = this.squares.ijToXy({ i, j });
@ -83,7 +87,7 @@ Board.prototype.drawRobots = function(robots) {
this.updateArrowVisibilities(); this.updateArrowVisibilities();
}; };
Board.prototype.drawArrow = function({ direction, label, i, j, left, top, parentId }) { Content.prototype.drawArrow = function({ direction, label, i, j, left, top, parentId }) {
const s = this.squares.sideLength; const s = this.squares.sideLength;
const arrow = document.createElement('div'); const arrow = document.createElement('div');
@ -102,7 +106,7 @@ Board.prototype.drawArrow = function({ direction, label, i, j, left, top, parent
return arrow; return arrow;
}; };
Board.prototype.drawSquares = function() { Content.prototype.drawSquares = function() {
for (let i = 0; i < this.squares.perSide; i++) { for (let i = 0; i < this.squares.perSide; i++) {
for (let j = 0; j < this.squares.perSide; j++) { for (let j = 0; j < this.squares.perSide; j++) {
// All squares are absolutely positioned relative to the viewport. // All squares are absolutely positioned relative to the viewport.
@ -120,18 +124,11 @@ Board.prototype.drawSquares = function() {
} }
}; };
Board.prototype.drawWalls = function(edges) { Content.prototype.drawWalls = function(edges) {
this.walls = {}; this.walls = {};
while (this.parent.getElementsByClassName('content-wall-x').length) { this.parent.querySelectorAll('.content-wall-x').forEach(el => el.parentNode.removeChild(el));
const child = this.parent.getElementsByClassName('content-wall-x')[0]; this.parent.querySelectorAll('.content-wall-y').forEach(el => el.parentNode.removeChild(el));
child.parentNode.removeChild(child);
}
while (this.parent.getElementsByClassName('content-wall-y').length) {
const child = this.parent.getElementsByClassName('content-wall-y')[0];
child.parentNode.removeChild(child);
}
edges.forEach(edge => { edges.forEach(edge => {
this.walls[edge] = true; this.walls[edge] = true;
@ -169,7 +166,7 @@ Board.prototype.drawWalls = function(edges) {
this.updateArrowVisibilities(); this.updateArrowVisibilities();
}; };
Board.prototype.moveRobot = function({ id, i, j }) { Content.prototype.moveRobot = function({ id, i, j }) {
const robot = document.getElementById(`robot-${id}`); const robot = document.getElementById(`robot-${id}`);
const arrows = document.getElementById(`arrows-${id}`); const arrows = document.getElementById(`arrows-${id}`);
@ -192,7 +189,7 @@ Board.prototype.moveRobot = function({ id, i, j }) {
document.dispatchEvent(event); document.dispatchEvent(event);
} }
Board.prototype.updateArrowVisibilities = function() { Content.prototype.updateArrowVisibilities = function() {
const keys = Object.keys(this.robots); const keys = Object.keys(this.robots);
keys.forEach(key => { keys.forEach(key => {
@ -219,7 +216,7 @@ Board.prototype.updateArrowVisibilities = function() {
}); });
} }
Board.prototype.onArrowClick = function(evt) { Content.prototype.onArrowClick = function(evt) {
const direction = evt.currentTarget.dataset.direction; const direction = evt.currentTarget.dataset.direction;
const id = evt.currentTarget.dataset.parent; const id = evt.currentTarget.dataset.parent;
@ -234,11 +231,15 @@ Board.prototype.onArrowClick = function(evt) {
this.moveRobot({ id, i: i2, j: j2 }) this.moveRobot({ id, i: i2, j: j2 })
}; };
Board.prototype.onReset = function() { Content.prototype.onReceiveGuess = function() {
console.log("Board onReceiveGuess()")
}
Content.prototype.onReset = function() {
this.drawRobots(this.initialRobotState); this.drawRobots(this.initialRobotState);
}; };
Board.prototype.findNextObstacle = function({ direction, i, j }) { Content.prototype.findNextObstacle = function({ direction, i, j }) {
switch (direction) { switch (direction) {
case 'right': case 'right':
for (let ii = i + 1; ii < this.squares.perSide; ii++) { for (let ii = i + 1; ii < this.squares.perSide; ii++) {

233
client/controls.js vendored

@ -1,101 +1,174 @@
//===== Constructor
const Controls = function() { const Controls = function() {
this.moves = 0; this.moves = 0;
this.names = {};
document.addEventListener('move-increment', this.onMoveIncrement.bind(this)); // this.buildGuesses();
// this.setUi('connecting');
document.getElementById('controls-moves-reset').addEventListener('click', this.onMoveReset.bind(this)); document.addEventListener('L-conn-open', this.msgJoin.bind(this));
document.getElementById('controls-next-round').addEventListener('click', this.onStartNextGame.bind(this));
document.getElementById('controls-regenerate-walls').addEventListener('click', this.onRegenWalls.bind(this));
document.getElementById('controls-reposition-robots').addEventListener('click', this.onRepositionRobots.bind(this));
// document.getElementById('controls-timer-skip').addEventListener('click', this.onSkipTimer.bind(this));
}
// guessBuild: = function() { // Message handlers: "Local message" and "Global message"
// const container = document.getElementById('controls-guesses'); // document.addEventListener('G-move', this.onMoveIncrement.bind(this));
// container.querySelectorAll('.controls-guess').forEach(el => el.parentNode.removeChild(el)); // document.addEventListener('G-win', this.onMoveIncrement.bind(this));
// document.addEventListener('G-start', this.msgStartGame.bind(this));
// document.addEventListener('G-stop', this.msgStartGame.bind(this));
// document.addEventListener('G-players', this.msgStartGame.bind(this)); <--- controls.playersUpdate(data.body)
// for (let i = 1; i <= 30; i++) { // document.addEventListener('G-walls', this.msgStartGame.bind(this));
// const guess = document.createElement('div'); // document.addEventListener('G-robots', this.msgStartGame.bind(this));
// guess.className = 'controls-guess'; // document.addEventListener('G-guess', this.msgStartGame.bind(this)); <--- controls.onReceiveGuess(data.body);
// guess.innerHTML = i;
// guess.setAttribute('data-value', i);
// guess.addEventListener('click', Controls.guessClick)
// container.appendChild(guess);
// }
// },
// guessClick: (evt) => { // document.addEventListener('L-move', this.onMoveIncrement.bind(this));
// alert(evt.currentTarget.dataset.value)
// },
Controls.prototype.playerAdd = function() { // document.addEventListener('L-conn-error', this.onConnectionError.bind(this));
const names = ["Biff", "Morty", "Herb", "Chester", "Lyle", "Cap", "Dale", "Ned", "Mindy"] // document.addEventListener('L-skip-timer', this.msgStartGame.bind(this));
const r = Math.floor(Math.random() * names.length); // document.addEventListener('L-skip-timer', this.msgStartGame.bind(this));
const rawInput = names[r] //prompt("What is your name?"); // Click handlers
// document.getElementById('controls-moves-reset').addEventListener('click', this.onMoveReset.bind(this));
const evt = new CustomEvent('playerAdd', { detail: rawInput }); // document.getElementById('controls-start-game').addEventListener('click', this.onStartGame.bind(this));
document.dispatchEvent(evt); // document.getElementById('controls-stop-game').addEventListener('click', this.onStopGame.bind(this));
}; // document.getElementById('controls-regenerate-walls').addEventListener('click', this.onRegenWalls.bind(this));
// document.getElementById('controls-reposition-robots').addEventListener('click', this.onRepositionRobots.bind(this));
// document.getElementById('controls-timer-skip').addEventListener('click', this.onSkipTimer.bind(this));
}
Controls.prototype.playerRemove = function(rawInput) { //===== UI
alert('nope')
};
Controls.prototype.playersUpdate = function(names) { Controls.prototype.drawGuesses = function() {
const container = document.getElementById('controls-players'); const container = document.getElementById('controls-guesses');
const keys = Object.keys(names); container.querySelectorAll('.controls-guess').forEach(el => el.parentNode.removeChild(el));
if (keys.length > 0) { for (let i = 1; i <= 30; i++) {
const nobody = document.getElementById('controls-players-nobody'); const guess = document.createElement('div');
nobody.parentNode.removeChild(nobody); guess.className = 'controls-guess';
guess.innerHTML = i;
guess.setAttribute('data-value', i);
guess.addEventListener('click', this.onClickGuess.bind(this))
container.appendChild(guess);
} }
};
keys.forEach(connectionId => { Controls.prototype.showWaiting = function() {
const id = `player-${connectionId}`; document.getElementById('controls-start').parentNode.style.display = '';
document.getElementById('controls-walls').parentNode.style.display = '';
if (document.getElementById(id)) { document.getElementById('controls-robots').parentNode.style.display = '';
return;
}
const n = document.createElement('div'); document.getElementById('controls-stop').parentNode.style.display = 'none';
n.id = id; // document.getElementById('controls-moves-reset').parentNode.style.display = 'none';
n.innerHTML = names[connectionId]; // document.getElementById('controls-timer-skip').parentNode.style.display = 'none';
n.className = 'controls-player'; // document.getElementById('controls-guesses').parentNode.style.display = 'none';
container.appendChild(n)
});
// container.querySelectorAll('.controls-player').forEach(el => {
// if (!names[el.id]) {
// container.removeChild(el);
// }
// })
}; };
Controls.prototype.onMoveIncrement = function() {
this.moves++;
document.getElementById('controls-moves').innerHTML = this.moves;
};
Controls.prototype.onMoveReset = function() { //===== Message handlers
this.moves = 0;
document.getElementById('controls-moves').innerHTML = this.moves;
const event = new Event('board-reset'); Controls.prototype.msgJoin = function() {
document.dispatchEvent(event); this.showWaiting();
}; };
Controls.prototype.onStartNextGame = function() { // Controls.prototype.playersUpdate = function(names) {
const evt = new Event('board-reset'); // const container = document.getElementById('controls-players');
document.dispatchEvent(evt); // const keys = Object.keys(names);
}
// if (keys.length > 0) {
Controls.prototype.onRegenWalls = function() { // const nobody = document.getElementById('controls-players-nobody');
const evt = new Event('regenerateWalls'); // nobody.parentNode.removeChild(nobody);
document.dispatchEvent(evt); // }
}
// keys.forEach(connectionId => {
Controls.prototype.onRepositionRobots = function() { // const id = `player-${connectionId}`;
const evt = new Event('repositionRobots');
document.dispatchEvent(evt); // if (document.getElementById(id)) {
} // return;
// }
// const n = document.createElement('div');
// n.id = id;
// n.innerHTML = names[connectionId];
// n.className = 'controls-player';
// container.appendChild(n)
// });
// console.log(names)
// this.names = names;
// // container.querySelectorAll('.controls-player').forEach(el => {
// // if (!names[el.id]) {
// // container.removeChild(el);
// // }
// // })
// };
// Controls.prototype.onReceiveGuess = function(message) {
// console.log(this.names[message.id] + ' has a solution')
// }
// Controls.prototype.onClickGuess = function(evt) {
// const e = new CustomEvent('guess', { detail: { moves: evt.currentTarget.dataset.value } });
// document.dispatchEvent(e);
// }
// Controls.prototype.onMoveIncrement = function() {
// this.moves++;
// document.getElementById('controls-moves').innerHTML = this.moves;
// };
// Controls.prototype.onMoveReset = function() {
// this.moves = 0;
// document.getElementById('controls-moves').innerHTML = this.moves;
// const event = new Event('board-reset');
// document.dispatchEvent(event);
// };
// Controls.prototype.onStartGame = function() {
// this.setUi('gameStarted');
// const evt = new Event('startGame');
// document.dispatchEvent(evt);
// }
// Controls.prototype.onStopGame = function() {
// this.setUi('gameStopped');
// const evt = new Event('stopGame');
// document.dispatchEvent(evt);
// }
// Controls.prototype.onRegenWalls = function() {
// const evt = new Event('regenerateWalls');
// document.dispatchEvent(evt);
// }
// Controls.prototype.onRepositionRobots = function() {
// const evt = new Event('repositionRobots');
// document.dispatchEvent(evt);
// }
// Controls.prototype.setUi = function(state) {
// switch(state) {
// case 'gameStarted':
// document.getElementById('controls-start-round').parentNode.style.display = 'none';
// document.getElementById('controls-regenerate-walls').parentNode.style.display = 'none';
// document.getElementById('controls-reposition-robots').parentNode.style.display = 'none';
// document.getElementById('controls-stop-round').parentNode.style.display = '';
// document.getElementById('controls-moves-reset').parentNode.style.display = '';
// document.getElementById('controls-timer-skip').parentNode.style.display = '';
// document.getElementById('controls-guesses').parentNode.style.display = '';
// break;
// case 'gameStopped':
// document.getElementById('controls-start-round').parentNode.style.display = '';
// document.getElementById('controls-regenerate-walls').parentNode.style.display = '';
// document.getElementById('controls-reposition-robots').parentNode.style.display = '';
// document.getElementById('controls-stop-round').parentNode.style.display = 'none';
// document.getElementById('controls-moves-reset').parentNode.style.display = 'none';
// document.getElementById('controls-timer-skip').parentNode.style.display = 'none';
// document.getElementById('controls-guesses').parentNode.style.display = 'none';
// break;
// }
// }

@ -0,0 +1,52 @@
const Join = function() {
document.addEventListener('L-conn-error', this.onConnectionError.bind(this));
document.addEventListener('L-conn-open', this.onConnectionOpen.bind(this));
document.getElementById('join-setup-start').addEventListener('click', this.onClickStart.bind(this));
document.getElementById('join-setup-go').addEventListener('click', this.onClickStart.bind(this));
document.getElementById('join-setup-back').addEventListener('click', this.onClickBack.bind(this));
this.showSetup();
};
Join.prototype.showSetup = function() {
document.getElementById('join-setup').style.display = 'block';
document.getElementById('join-connect').style.display = 'none';
document.getElementById('join-error').style.display = 'none';
};
Join.prototype.showLoad = function() {
document.getElementById('join-setup').style.display = 'none';
document.getElementById('join-connect').style.display = 'block';
document.getElementById('join-error').style.display = 'none';
};
Join.prototype.showError = function() {
document.getElementById('join-setup').style.display = 'none';
document.getElementById('join-connect').style.display = 'none';
document.getElementById('join-error').style.display = 'block';
};
Join.prototype.onClickBack = function() {
this.showSetup();
};
Join.prototype.onClickGo = function() {
this.showLoad();
};
Join.prototype.onClickStart = function() {
this.showLoad();
const evt = new Event('L-join');
document.dispatchEvent(evt);
};
Join.prototype.onConnectionError = function() {
this.showError();
};
Join.prototype.onConnectionOpen = function() {
document.getElementById('join').style.display = 'none';
this.showSetup();
};

@ -1,3 +0,0 @@
const Settings = function() {
this.squaresPerSide = 20;
}

@ -0,0 +1,53 @@
#content-container {
background-color: #28313b;
background-image: linear-gradient(315deg, #28313b 0%, #1A2026 74%);
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: 0;
}
.content-square {
background: #ddd;
border-style: solid;
border-color: #aaa;
border-width: 0 1px 1px 0;
position: absolute;
}
.content-wall-x {
background: #222;
height: 8px;
margin-top: -4px;
position: absolute;
}
.content-wall-y {
background: #222;
margin-left: -4px;
position: absolute;
width: 8px;
}
.content-robot {
position: absolute;
transition: left 0.4s cubic-bezier(0,1,.5,1), top 0.4s cubic-bezier(0,1,.5,1);
}
.content-arrows {
transition: left 0.4s cubic-bezier(0,1,.5,1), top 0.4s cubic-bezier(0,1,.5,1);
}
.content-arrow {
color: #aaa;
cursor: pointer;
position: absolute;
text-align: center;
user-select: none;
}
.content-arrow:hover {
color: #000;
}

@ -0,0 +1,105 @@
#controls-container {
background: #e7e7e7;
border: solid #e7e7e7;
border-width: 0 10px;
bottom: 0;
left: 20px;
position: absolute;
top: 0;
width: 300px;
z-index: 1;
}
.controls-title {
background-color: #639978;
background-image: url('sprite-robots.png');
/*color: #fff;*/
font-size: 12px;
line-height: 48px;
margin-bottom: 24px;
text-align: center;
}
.controls-subtitle {
background-color: #4D3243;
background-image: linear-gradient(90deg, #4D3243, #3C2132);
color: #fff;
padding: 12px;
margin: 24px 0 12px 0;
}
.controls-alert {
background: #21dfae;
color: #222;
padding: 12px;
margin: 24px 0 12px 0;
}
.controls-row {
align-items: center;
display: flex;
flex-direction: row;
padding: 8px;
}
.controls-row :nth-child(2) {
flex: 1;
margin: 0 8px;
width: 100%;
}
#controls-players {
}
.controls-player {
padding: 8px;
}
#controls-local {
}
.controls-guess {
background: none;
cursor: pointer;
float: left;
font-size: 12px;
height: 30px;
line-height: 30px;
text-align: center;
width: 30px;
}
.controls-guess:hover {
background: #21dfae;
}
#controls-footer {
background: #9A748C;
bottom: 0;
left: 0;
padding: 24px;
position: absolute;
right: 0;
}
.controls-button {
background: none;
border: 1px solid #888;
color: #444;
cursor: pointer;
font-size: 12px;
padding: 4px 8px;
}
.controls-button:hover {
background: #21dfae;
border-color: #21dfae;
color: #222;
}
#controls-start {
display: block;
margin: 0 auto;
padding: 4px 8px;
}

@ -8,167 +8,13 @@ body {
} }
/* /*
4D3243 PALETTE:
21DFAE Aquamarine
4D3243 Plum
DE4F21 Fire
EXPERIMENTAL:
9A748C 9A748C
639699 639699
364B4D 364B4D
*/ */
#controls-container {
background: #e7e7e7;
border: solid #e7e7e7;
border-width: 0 10px;
bottom: 0;
left: 20px;
position: absolute;
top: 0;
width: 300px;
z-index: 1;
}
.controls-title {
background-color: #639978;
background-image: url('sprite-robots.png');
/*color: #fff;*/
font-size: 12px;
line-height: 48px;
margin-bottom: 24px;
text-align: center;
}
.controls-subtitle {
background-color: #4D3243;
background-image: linear-gradient(90deg, #4D3243, #3C2132);
color: #fff;
padding: 12px;
margin: 24px 0 12px 0;
}
.controls-row {
align-items: center;
display: flex;
flex-direction: row;
padding: 8px;
}
.controls-row :nth-child(2) {
flex: 1;
margin: 0 8px;
width: 100%;
}
.controls-button {
background: none;
border: 1px solid #888;
color: #444;
cursor: pointer;
font-size: 12px;
padding: 4px 8px;
}
.controls-button:hover {
background: #639699;
border-color: #639699;
color: #fff;
}
#controls-start {
display: block;
margin: 0 auto;
padding: 4px 8px;
}
.controls-room-error {
background: #e00000;
color: #fff;
margin: 4px 0;
padding: 8px;
}
#controls-players {
}
.controls-player {
padding: 8px;
}
#controls-guesses {
}
.controls-guess {
background: yellow;
float: left;
font-size: 12px;
height: 30px;
line-height: 30px;
text-align: center;
width: 30px;
}
.controls-guess:hover {
background: orange;
}
#controls-footer {
background: #9A748C;
bottom: 0;
left: 0;
padding: 24px;
position: absolute;
right: 0;
}
#content-container {
background-color: #28313b;
background-image: linear-gradient(315deg, #28313b 0%, #1A2026 74%);
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
z-index: 0;
}
.content-square {
background: #ddd;
border-style: solid;
border-color: #aaa;
border-width: 0 1px 1px 0;
position: absolute;
}
.content-wall-x {
background: #222;
height: 8px;
margin-top: -4px;
position: absolute;
}
.content-wall-y {
background: #222;
margin-left: -4px;
position: absolute;
width: 8px;
}
.content-robot {
position: absolute;
transition: left 0.4s cubic-bezier(0,1,.5,1), top 0.4s cubic-bezier(0,1,.5,1);
}
.content-arrows {
transition: left 0.4s cubic-bezier(0,1,.5,1), top 0.4s cubic-bezier(0,1,.5,1);
}
.content-arrow {
color: #aaa;
cursor: pointer;
position: absolute;
text-align: center;
user-select: none;
}
.content-arrow:hover {
color: #000;
}

@ -3,43 +3,64 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title> <title>Robots</title>
<link rel="stylesheet" href="index.css"> <link rel="stylesheet" href="index.css">
<link rel="stylesheet" href="controls.css">
<link rel="stylesheet" href="content.css">
<link rel="stylesheet" href="join.css">
<link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
<script type='text/javascript' src='client/board.js'></script> <script type='text/javascript' src='client/content.js'></script>
<script type='text/javascript' src='client/connection.js'></script>
<script type='text/javascript' src='client/controls.js'></script> <script type='text/javascript' src='client/controls.js'></script>
<script type='text/javascript' src='client/cookie.js'></script> <script type='text/javascript' src='client/cookie.js'></script>
<script type='text/javascript' src='client/join.js'></script>
<script type='text/javascript' src='client/squares.js'></script> <script type='text/javascript' src='client/squares.js'></script>
</head> </head>
<body> <body>
<div id="join">
<div id='join-setup'>
<button type='button' class='join-button' id='join-setup-start'>Start a new game</button>
<div class='join-message'>- or -</div>
<input type="text" placeholder="Join a game" />
<button type='text' class='join-button' id='join-setup-go'>Go</button>
</div>
<div id="join-connect">
<div class='join-message'>Contacting Server</div>
</div>
<div id="join-error">
<div class='join-message'>Can't reach the server.</div>
<button type='text' class='join-button' id='join-setup-back'>Back</button>
</div>
</div>
<div id="controls-container"> <div id="controls-container">
<div id="controls-manage"> <div id="controls-manage">
<div class='controls-subtitle'>Manage</div> <div class='controls-subtitle'>Global</div>
<div class="controls-row"> <div class="controls-row">
<div>Room ID</div> <div>Start Next Round</div>
<input type="text" id='game-id'> <div>&nbsp;</div>
<div class="controls-button">Go</div> <div class='controls-button' id='controls-start'>Do It</div>
</div> </div>
<div class="controls-room-errors"></div>
<div class="controls-row"> <div class="controls-row">
<div>Start Next Round</div> <div>Stop This Round</div>
<div>&nbsp;</div> <div>&nbsp;</div>
<div class='controls-button' id='controls-next-round'>Do It</div> <div class='controls-button' id='controls-stop'>Yeah</div>
</div> </div>
<div class="controls-row"> <div class="controls-row">
<div>Move Robots</div> <div>Move Robots</div>
<div>&nbsp;</div> <div>&nbsp;</div>
<div class='controls-button' id='controls-reposition-robots'>Reposition</div> <div class='controls-button' id='controls-robots'>Reposition</div>
</div> </div>
<div class="controls-row"> <div class="controls-row">
<div>Move Walls</div> <div>Move Walls</div>
<div>&nbsp;</div> <div>&nbsp;</div>
<div class='controls-button' id='controls-regenerate-walls'>Regenerate</div> <div class='controls-button' id='controls-walls'>Regenerate</div>
</div> </div>
</div> </div>
@ -50,8 +71,8 @@
</div> </div>
<div id="controls-guesses"> <div id="controls-local">
<div class='controls-subtitle'>Guess</div> <div class='controls-subtitle'>Local</div>
<div class="controls-row"> <div class="controls-row">
<div>Moves:</div> <div>Moves:</div>
<div id="controls-moves">4</div> <div id="controls-moves">4</div>
@ -63,6 +84,10 @@
<div id="controls-timer">0:42</div> <div id="controls-timer">0:42</div>
<div class='controls-button' id='controls-timer-skip'>Skip</div> <div class='controls-button' id='controls-timer-skip'>Skip</div>
</div> </div>
<div id="controls-guesses">
<div class="controls-alert">How many moves to win?</div>
</div>
</div> </div>
<div id="controls-footer"> <div id="controls-footer">
@ -70,72 +95,18 @@
</div> </div>
</div> </div>
<div id="content-container"> <div id="content-container"></div>
</div>
<script> <script>
window.addEventListener('load', () => { window.addEventListener('load', () => {
const connection = new WebSocket('ws://localhost:8080/ricochet');
const squares = new Squares(); const squares = new Squares();
const controls = new Controls(connection); new Join();
const board = new Board({ parent: document.getElementById('content-container'), squares });
connection.onopen = controls.playerAdd.bind(controls);
document.addEventListener('repositionRobots', () => {
connection.send(JSON.stringify({ head: { type: 'repositionRobots' }}));
});
document.addEventListener('regenerateWalls', () => {
connection.send(JSON.stringify({ head: { type: 'regenerateWalls' }}));
});
document.addEventListener('playerAdd', (evt) => {
connection.send(JSON.stringify({ head: { type: 'playerAdd' }, rawBody: evt.detail }));
});
connection.onerror = (err) => {
console.error(err);
const div = document.createElement('div');
div.className = 'controls-room-error';
div.innerHTML = "Can't reach the server.";
document.getElementById('controls-room').appendChild(div);
}
connection.onmessage = function (msg) {
const data = JSON.parse(msg.data)
if (!data.head || !data.body) {
console.warn("Unprocessable entity: ", msg)
return;
}
console.log(msg)
switch(data.head.type) {
case 'connect':
break;
case 'disconnect':
break;
case 'players':
controls.playersUpdate(data.body)
break;
case 'robots': new Connection();
board.drawRobots(data.body);
break;
case 'walls': new Controls();
board.drawWalls(data.body);
break;
default: new Content({ parent: document.getElementById('content-container'), squares });
console.warn("Unhandled message: ", msg)
}
};
}) })
</script> </script>
</body> </body>

@ -0,0 +1,102 @@
#join {
background-color: #28313b;
background-image: linear-gradient(315deg, #28313b 0%, #1A2026 74%);
bottom: 0;
color: #e7e7e7;
display: flex;
flex-direction: column;
justify-content: center;
left: 0;
position: absolute;
right: 0;
text-align: center;
top: 0;
z-index: 10;
}
.join-button {
background: none;
border: 1px solid #888;
color: #ccc;
cursor: pointer;
font-size: 32px;
height: 72px;
line-height: 72px;
vertical-align: middle;
}
.join-button:hover {
background: #21dfae;
border-color: #21dfae;
color: #222;
}
.join-message {
font-size: 24px;
line-height: 72px;
}
#join-setup-start,
#join-setup-back {
width: 500px;
}
#join-setup-go {
width: 96px;
}
#join-setup .join-message {
margin: 24px 0;
}
#join-setup input {
border: 0;
font-size: 32px;
height: 72px;
line-height: 72px;
margin-right: 40px;
padding: 0 24px;
vertical-align: middle;
width: 360px;
}
@keyframes LoadingBackground {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
#join-connect {
animation: LoadingBackground 8s infinite;
background-size: 230% 230%;
background-image: linear-gradient(90deg, #21DFAE 600px, #FFF 600px, #FFF 605px, #21DFAE 605px);
color: #222;
display: none;
font-size: 32px;
height: 72px;
margin: 0 auto;
text-align: center;
width: 500px;
}
#join-error {
display: none;
font-size: 32px;
height: 264px;
line-height: 264px;
margin: 0 auto;
position: relative;
text-align: center;
width: 500px;
}
#join-error .join-message {
background-color: #DE4F21;
color: #fff;
}
#join-error button {
position: absolute;
bottom: 0;
left: 0;
}

@ -1,10 +1,12 @@
// TODO dynamic move population
// TODO move websocket server to /core // TODO move websocket server to /core
// TODO dynamic socket server resolution // TODO dynamic socket server resolution
// TODO namespace server to /ricochet // TODO namespace server to /ricochet
// TODO your favorite games // TODO cookie room link
// TODO no cancel from name prompt // TODO no cancel from name prompt
// TODO limit concurrent players // TODO limit concurrent players, make sure connections are closed
// TODO window resize update board // TODO window resize update board
// TODO donate link // TODO donate link
// TODO tutorial // TODO tutorial
// TODO walls algorigthm
// TODO fix overlapping robot arrows
// TODO win declare/add/remove

@ -1,4 +1,5 @@
const WebSocket = require('ws'); const WebSocket = require('ws');
const url = require('url');
const uuid = require('node-uuid'); const uuid = require('node-uuid');
const jsDir = `${__dirname}/server`; const jsDir = `${__dirname}/server`;
@ -14,8 +15,17 @@ const G = new Game();
const Server = { const Server = {
games: {}, games: {},
messageOne: (ws, message) => {
DEBUG && console.log(`Sending to only ${ws.id}:`);
DEBUG && console.log(message);
ws.send(JSON.stringify(message));
},
messageOthers: (ws, message) => { messageOthers: (ws, message) => {
DEBUG && console.log("Sending to other " + wss.clients.size + " clients.") DEBUG && console.log(`Sending to other ${wss.clients.size - 1} client(s):`);
DEBUG && console.log(message);
wss.clients.forEach((client) => { wss.clients.forEach((client) => {
if (client !== ws && client.readyState === WebSocket.OPEN) { if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(message)); client.send(JSON.stringify(message));
@ -24,7 +34,9 @@ const Server = {
}, },
messageAll: (message) => { messageAll: (message) => {
DEBUG && console.log("Sending to all " + wss.clients.size + " clients.") DEBUG && console.log(`Sending to all ${wss.clients.size} client(s):`);
DEBUG && console.log(message);
wss.clients.forEach((client) => { wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) { if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(message)); client.send(JSON.stringify(message));
@ -33,27 +45,28 @@ const Server = {
}, },
onDisconnect: (ws) => { onDisconnect: (ws) => {
DEBUG && console.log('Disconnected ' + ws.id); DEBUG && console.log("Disconnected:");
Server.messageOthers(ws, { head: { type: 'disconnect' }, body: { id: ws.id }})
DEBUG && console.log('Removing player: ');
DEBUG && console.log(ws.id); DEBUG && console.log(ws.id);
G.removePlayer(ws.id); G.removePlayer(ws.id);
Server.messageOthers({ head: { type: 'players' }, body: G.getPlayers() }); Server.messageOthers({ type: 'players', body: G.getPlayers() });
}, },
onConnect: (ws, req) => { onConnect: (ws, req) => {
ws.id = uuid.v4(); ws.id = uuid.v4();
DEBUG && console.log(req.url + ' connected ' + ws.id);
ws.on('message', Server.onMessage.bind(null, ws)); ws.on('message', Server.onMessage.bind(null, ws));
ws.on('close', Server.onDisconnect.bind(null, ws)) ws.on('close', Server.onDisconnect.bind(null, ws));
Server.messageAll({ head: { type: 'connect' }, body: { id: ws.id }}); const query = url.parse(req.url, true).query;
Server.messageAll({ head: { type: 'walls' }, body: G.getWalls()}); const santizedName = (query.name || 'Unknown').replace(/[^\w ]/g, '');
Server.messageAll({ head: { type: 'robots' }, body: G.getRobots()});
DEBUG && console.log("Connected:");
DEBUG && console.log (`${santizedName} ${ws.id} via ${req.url}`);
G.addPlayer(ws.id, santizedName);
Server.messageAll({ type: 'players', body: G.getPlayers() });
Server.messageOne(ws, { type: 'walls', body: G.getWalls()});
Server.messageOne(ws, { type: 'robots', body: G.getRobots()});
}, },
onMessage: (ws, json) => { onMessage: (ws, json) => {
@ -69,24 +82,20 @@ const Server = {
} }
switch (message.head.type) { switch (message.head.type) {
case 'playerAdd': case 'guess':
DEBUG && console.log('Adding player: '); Server.messageAll({ type: 'guess', guess: 29, id: ws.id });
DEBUG && console.log(ws.id);
DEBUG && console.log(message.rawBody);
const santizedName = message.rawBody.replace(/[^\w ]/g, '');
G.addPlayer(ws.id, santizedName);
Server.messageAll({ head: { type: 'players' }, body: G.getPlayers() });
break; break;
case 'playerRemove': case 'robots':
Server.messageAll({ type: 'robots', body: G.getRobots()});
break; break;
case 'repositionRobots': case 'walls':
Server.messageAll({ head: { type: 'robots' }, body: G.getRobots()}); Server.messageAll({ type: 'walls', body: G.getWalls()});
break; break;
case 'regenerateWalls': case 'start':
Server.messageAll({ head: { type: 'walls' }, body: G.getWalls()}); Server.messageAll({ type: 'start' });
break; break;
case 'newRound': case 'stop':
Server.messageAll({ type: 'stop' });
break; break;
default: default:
console.warn("Unknown message type: ", message.head.type) console.warn("Unknown message type: ", message.head.type)

@ -49,7 +49,7 @@ Game.prototype.getWalls = function() {
// "19-9-19-10" // "19-9-19-10"
// ]; // ];
console.log("Generating walls."); // console.log("Generating walls.");
// Squares per side has quadratic relationship with wall/corner requirements. // Squares per side has quadratic relationship with wall/corner requirements.
const squaresPerSide = 20; const squaresPerSide = 20;

Loading…
Cancel
Save