Join screen UI.

master
Ben Burlingham 5 years ago
parent 82f9604043
commit 9afd3d8364
  1. 81
      client/connection.js
  2. 61
      client/content.js
  3. 237
      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. 123
      index.html
  10. 102
      join.css
  11. 10
      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.squares = squares;
@ -7,24 +9,26 @@ const Board = function({ parent, squares }) {
this.listeners = {};
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.robots = {};
while (this.parent.getElementsByClassName('content-robot').length) {
const child = this.parent.getElementsByClassName('content-robot')[0];
child.parentNode.removeChild(child);
}
while (this.parent.getElementsByClassName('content-arrows').length) {
const child = this.parent.getElementsByClassName('content-arrows')[0];
child.parentNode.removeChild(child);
}
this.parent.querySelectorAll('.content-robot').forEach(el => el.parentNode.removeChild(el));
this.parent.querySelectorAll('.content-arrows').forEach(el => el.parentNode.removeChild(el));
robots.forEach(({ color, i, j }) => {
const { x, y } = this.squares.ijToXy({ i, j });
@ -83,7 +87,7 @@ Board.prototype.drawRobots = function(robots) {
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 arrow = document.createElement('div');
@ -102,7 +106,7 @@ Board.prototype.drawArrow = function({ direction, label, i, j, left, top, parent
return arrow;
};
Board.prototype.drawSquares = function() {
Content.prototype.drawSquares = function() {
for (let i = 0; i < this.squares.perSide; i++) {
for (let j = 0; j < this.squares.perSide; j++) {
// 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 = {};
while (this.parent.getElementsByClassName('content-wall-x').length) {
const child = this.parent.getElementsByClassName('content-wall-x')[0];
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);
}
this.parent.querySelectorAll('.content-wall-x').forEach(el => el.parentNode.removeChild(el));
this.parent.querySelectorAll('.content-wall-y').forEach(el => el.parentNode.removeChild(el));
edges.forEach(edge => {
this.walls[edge] = true;
@ -169,7 +166,7 @@ Board.prototype.drawWalls = function(edges) {
this.updateArrowVisibilities();
};
Board.prototype.moveRobot = function({ id, i, j }) {
Content.prototype.moveRobot = function({ id, i, j }) {
const robot = document.getElementById(`robot-${id}`);
const arrows = document.getElementById(`arrows-${id}`);
@ -192,7 +189,7 @@ Board.prototype.moveRobot = function({ id, i, j }) {
document.dispatchEvent(event);
}
Board.prototype.updateArrowVisibilities = function() {
Content.prototype.updateArrowVisibilities = function() {
const keys = Object.keys(this.robots);
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 id = evt.currentTarget.dataset.parent;
@ -234,11 +231,15 @@ Board.prototype.onArrowClick = function(evt) {
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);
};
Board.prototype.findNextObstacle = function({ direction, i, j }) {
Content.prototype.findNextObstacle = function({ direction, i, j }) {
switch (direction) {
case 'right':
for (let ii = i + 1; ii < this.squares.perSide; ii++) {

237
client/controls.js vendored

@ -1,101 +1,174 @@
//===== Constructor
const Controls = function() {
this.moves = 0;
this.names = {};
document.addEventListener('move-increment', this.onMoveIncrement.bind(this));
document.getElementById('controls-moves-reset').addEventListener('click', this.onMoveReset.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() {
// const container = document.getElementById('controls-guesses');
// container.querySelectorAll('.controls-guess').forEach(el => el.parentNode.removeChild(el));
// this.buildGuesses();
// this.setUi('connecting');
// for (let i = 1; i <= 30; i++) {
// const guess = document.createElement('div');
// guess.className = 'controls-guess';
// guess.innerHTML = i;
// guess.setAttribute('data-value', i);
// guess.addEventListener('click', Controls.guessClick)
// container.appendChild(guess);
// }
// },
document.addEventListener('L-conn-open', this.msgJoin.bind(this));
// guessClick: (evt) => {
// alert(evt.currentTarget.dataset.value)
// },
// Message handlers: "Local message" and "Global message"
// document.addEventListener('G-move', this.onMoveIncrement.bind(this));
// 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)
// document.addEventListener('G-walls', this.msgStartGame.bind(this));
// document.addEventListener('G-robots', this.msgStartGame.bind(this));
// document.addEventListener('G-guess', this.msgStartGame.bind(this)); <--- controls.onReceiveGuess(data.body);
Controls.prototype.playerAdd = function() {
const names = ["Biff", "Morty", "Herb", "Chester", "Lyle", "Cap", "Dale", "Ned", "Mindy"]
const r = Math.floor(Math.random() * names.length);
// document.addEventListener('L-move', this.onMoveIncrement.bind(this));
// document.addEventListener('L-conn-error', this.onConnectionError.bind(this));
// document.addEventListener('L-skip-timer', this.msgStartGame.bind(this));
// document.addEventListener('L-skip-timer', this.msgStartGame.bind(this));
// Click handlers
// document.getElementById('controls-moves-reset').addEventListener('click', this.onMoveReset.bind(this));
// document.getElementById('controls-start-game').addEventListener('click', this.onStartGame.bind(this));
// 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));
}
const rawInput = names[r] //prompt("What is your name?");
//===== UI
const evt = new CustomEvent('playerAdd', { detail: rawInput });
document.dispatchEvent(evt);
};
Controls.prototype.drawGuesses = function() {
const container = document.getElementById('controls-guesses');
container.querySelectorAll('.controls-guess').forEach(el => el.parentNode.removeChild(el));
Controls.prototype.playerRemove = function(rawInput) {
alert('nope')
for (let i = 1; i <= 30; i++) {
const guess = document.createElement('div');
guess.className = 'controls-guess';
guess.innerHTML = i;
guess.setAttribute('data-value', i);
guess.addEventListener('click', this.onClickGuess.bind(this))
container.appendChild(guess);
}
};
Controls.prototype.playersUpdate = function(names) {
const container = document.getElementById('controls-players');
const keys = Object.keys(names);
Controls.prototype.showWaiting = function() {
document.getElementById('controls-start').parentNode.style.display = '';
document.getElementById('controls-walls').parentNode.style.display = '';
document.getElementById('controls-robots').parentNode.style.display = '';
if (keys.length > 0) {
const nobody = document.getElementById('controls-players-nobody');
nobody.parentNode.removeChild(nobody);
}
keys.forEach(connectionId => {
const id = `player-${connectionId}`;
if (document.getElementById(id)) {
return;
}
const n = document.createElement('div');
n.id = id;
n.innerHTML = names[connectionId];
n.className = 'controls-player';
container.appendChild(n)
});
// container.querySelectorAll('.controls-player').forEach(el => {
// if (!names[el.id]) {
// container.removeChild(el);
// }
// })
document.getElementById('controls-stop').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';
};
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;
//===== Message handlers
const event = new Event('board-reset');
document.dispatchEvent(event);
Controls.prototype.msgJoin = function() {
this.showWaiting();
};
Controls.prototype.onStartNextGame = function() {
const evt = new Event('board-reset');
document.dispatchEvent(evt);
}
Controls.prototype.onRegenWalls = function() {
const evt = new Event('regenerateWalls');
document.dispatchEvent(evt);
}
// Controls.prototype.playersUpdate = function(names) {
// const container = document.getElementById('controls-players');
// const keys = Object.keys(names);
Controls.prototype.onRepositionRobots = function() {
const evt = new Event('repositionRobots');
document.dispatchEvent(evt);
}
// if (keys.length > 0) {
// const nobody = document.getElementById('controls-players-nobody');
// nobody.parentNode.removeChild(nobody);
// }
// keys.forEach(connectionId => {
// const id = `player-${connectionId}`;
// 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
639699
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>
<meta charset="UTF-8">
<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="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">
<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/cookie.js'></script>
<script type='text/javascript' src='client/join.js'></script>
<script type='text/javascript' src='client/squares.js'></script>
</head>
<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-manage">
<div class='controls-subtitle'>Manage</div>
<div class='controls-subtitle'>Global</div>
<div class="controls-row">
<div>Room ID</div>
<input type="text" id='game-id'>
<div class="controls-button">Go</div>
<div>Start Next Round</div>
<div>&nbsp;</div>
<div class='controls-button' id='controls-start'>Do It</div>
</div>
<div class="controls-room-errors"></div>
<div class="controls-row">
<div>Start Next Round</div>
<div>Stop This Round</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 class="controls-row">
<div>Move Robots</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 class="controls-row">
<div>Move Walls</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>
@ -50,8 +71,8 @@
</div>
<div id="controls-guesses">
<div class='controls-subtitle'>Guess</div>
<div id="controls-local">
<div class='controls-subtitle'>Local</div>
<div class="controls-row">
<div>Moves:</div>
<div id="controls-moves">4</div>
@ -63,6 +84,10 @@
<div id="controls-timer">0:42</div>
<div class='controls-button' id='controls-timer-skip'>Skip</div>
</div>
<div id="controls-guesses">
<div class="controls-alert">How many moves to win?</div>
</div>
</div>
<div id="controls-footer">
@ -70,72 +95,18 @@
</div>
</div>
<div id="content-container">
</div>
<div id="content-container"></div>
<script>
window.addEventListener('load', () => {
const connection = new WebSocket('ws://localhost:8080/ricochet');
const squares = new Squares();
const controls = new Controls(connection);
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':
board.drawRobots(data.body);
break;
case 'walls':
board.drawWalls(data.body);
break;
default:
console.warn("Unhandled message: ", msg)
}
};
new Join();
new Connection();
new Controls();
new Content({ parent: document.getElementById('content-container'), squares });
})
</script>
</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 dynamic socket server resolution
// TODO namespace server to /ricochet
// TODO your favorite games
// TODO cookie room link
// 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 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 url = require('url');
const uuid = require('node-uuid');
const jsDir = `${__dirname}/server`;
@ -14,8 +15,17 @@ const G = new Game();
const Server = {
games: {},
messageOne: (ws, message) => {
DEBUG && console.log(`Sending to only ${ws.id}:`);
DEBUG && console.log(message);
ws.send(JSON.stringify(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) => {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(message));
@ -24,7 +34,9 @@ const Server = {
},
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) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(message));
@ -33,27 +45,28 @@ const Server = {
},
onDisconnect: (ws) => {
DEBUG && console.log('Disconnected ' + ws.id);
Server.messageOthers(ws, { head: { type: 'disconnect' }, body: { id: ws.id }})
DEBUG && console.log("Disconnected:");
DEBUG && console.log(ws.id);
DEBUG && console.log('Removing player: ');
DEBUG && console.log(ws.id);
G.removePlayer(ws.id);
Server.messageOthers({ head: { type: 'players' }, body: G.getPlayers() });
Server.messageOthers({ type: 'players', body: G.getPlayers() });
},
onConnect: (ws, req) => {
ws.id = uuid.v4();
DEBUG && console.log(req.url + ' connected ' + ws.id);
ws.on('message', Server.onMessage.bind(null, ws));
ws.on('close', Server.onDisconnect.bind(null, ws))
ws.on('close', Server.onDisconnect.bind(null, ws));
const query = url.parse(req.url, true).query;
const santizedName = (query.name || 'Unknown').replace(/[^\w ]/g, '');
DEBUG && console.log("Connected:");
DEBUG && console.log (`${santizedName} ${ws.id} via ${req.url}`);
G.addPlayer(ws.id, santizedName);
Server.messageAll({ head: { type: 'connect' }, body: { id: ws.id }});
Server.messageAll({ head: { type: 'walls' }, body: G.getWalls()});
Server.messageAll({ head: { type: 'robots' }, body: G.getRobots()});
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) => {
@ -69,24 +82,20 @@ const Server = {
}
switch (message.head.type) {
case 'playerAdd':
DEBUG && console.log('Adding player: ');
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() });
case 'guess':
Server.messageAll({ type: 'guess', guess: 29, id: ws.id });
break;
case 'playerRemove':
case 'robots':
Server.messageAll({ type: 'robots', body: G.getRobots()});
break;
case 'repositionRobots':
Server.messageAll({ head: { type: 'robots' }, body: G.getRobots()});
case 'walls':
Server.messageAll({ type: 'walls', body: G.getWalls()});
break;
case 'regenerateWalls':
Server.messageAll({ head: { type: 'walls' }, body: G.getWalls()});
case 'start':
Server.messageAll({ type: 'start' });
break;
case 'newRound':
case 'stop':
Server.messageAll({ type: 'stop' });
break;
default:
console.warn("Unknown message type: ", message.head.type)

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

Loading…
Cancel
Save