parent
82f9604043
commit
9afd3d8364
13 changed files with 677 additions and 385 deletions
@ -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,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; |
||||
} |
@ -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 |
Loading…
Reference in new issue