diff --git a/client/connection.js b/client/connection.js index 715062e..eaa0382 100644 --- a/client/connection.js +++ b/client/connection.js @@ -2,16 +2,28 @@ const Connection = function() { // Local event listeners - document.addEventListener('L-robots', () => { - this.ws.send(JSON.stringify({ head: { type: 'reposition-robots' }})); + document.addEventListener('L-start', () => { + this.ws.send(JSON.stringify({ type: 'start' })); }); - document.addEventListener('L-walls', () => { - this.ws.send(JSON.stringify({ head: { type: 'regenerate-walls' }})); + document.addEventListener('L-stop', () => { + this.ws.send(JSON.stringify({ type: 'stop' })); + }); + + document.addEventListener('L-skip', () => { + this.ws.send(JSON.stringify({ type: 'skip' })); }); document.addEventListener('L-guess', (evt) => { - this.ws.send(JSON.stringify({ head: { type: 'guess' }, rawBody: evt.detail })); + this.ws.send(JSON.stringify({ type: 'guess', rawBody: evt.detail })); + }); + + document.addEventListener('L-robots', () => { + this.ws.send(JSON.stringify({ type: 'robots' })); + }); + + document.addEventListener('L-walls', () => { + this.ws.send(JSON.stringify({ type: 'walls' })); }); document.addEventListener('L-join', (evt) => { @@ -20,7 +32,8 @@ const Connection = function() { }; Connection.prototype.connect = function(){ - const names = ["Biff", "Morty", "Herb", "Chester", "Lyle", "Cap", "Dale", "Ned", "Mindy"] + const names = ["Biff", "Morty", "Herb", "Chester", "Lyle", "Cap", "Dale", "Ned", "Mindy", "Frankie", "Gabriel", "Mona", "Dolores", + "Sepulveda", "Venus", "Blingbing", "Cyrpt"] const r = Math.floor(Math.random() * names.length); const rawInput = names[r] //prompt("What is your name?"); @@ -57,21 +70,13 @@ Connection.prototype.onReceiveMessage = function({ data }) { 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; + case 'guess': eventName = 'G-guess'; break; + case 'players': eventName = 'G-players'; break; + case 'robots': eventName = 'G-robots'; break; + case 'skip': eventName = 'G-skip'; break; + case 'start': eventName = 'G-start'; break; + case 'stop': eventName = 'G-stop'; break; + case 'walls': eventName = 'G-walls'; break; } if (eventName) { diff --git a/client/content.js b/client/content.js index 37f4429..5803c2f 100644 --- a/client/content.js +++ b/client/content.js @@ -11,7 +11,8 @@ const Content = function({ parent, squares }) { // this.drawSquares(); // document.addEventListener('L-conn-open', this.msgStartGame.bind(this)); - +// document.addEventListener('G-walls', this.msgStart.bind(this)); + // document.addEventListener('G-robots', this.msgStart.bind(this)); // document.addEventListener('board-reset', this.onReset.bind(this)); // from connection: board.drawRobots(data.body); // from connection: board.drawWalls(data.body); diff --git a/client/controls.js b/client/controls.js index 602f1ec..9ac3785 100644 --- a/client/controls.js +++ b/client/controls.js @@ -4,39 +4,58 @@ const Controls = function() { this.moves = 0; this.names = {}; - // this.buildGuesses(); - // this.setUi('connecting'); + this.drawGuesses(); document.addEventListener('L-conn-open', this.msgJoin.bind(this)); + document.addEventListener('L-move', this.msgMove.bind(this)); // 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-move', this.msgMove.bind(this)); + // document.addEventListener('G-win', this.msgWin.bind(this)); + document.addEventListener('G-guess', this.msgGuess.bind(this)); + document.addEventListener('G-skip', this.msgSkip.bind(this)); + document.addEventListener('G-start', this.msgStart.bind(this)); + document.addEventListener('G-stop', this.msgStop.bind(this)); + document.addEventListener('G-players', this.msgPlayers.bind(this)); - // 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); - - // 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)); + document.getElementById('controls-start').addEventListener('click', this.onClickStart.bind(this)); + document.getElementById('controls-stop').addEventListener('click', this.onClickStop.bind(this)); + document.getElementById('controls-walls').addEventListener('click', this.onClickWalls.bind(this)); + document.getElementById('controls-robots').addEventListener('click', this.onClickRobots.bind(this)); + document.getElementById('controls-reset').addEventListener('click', this.onClickReset.bind(this)); + document.getElementById('controls-skip').addEventListener('click', this.onClickSkip.bind(this)); } //===== UI +Controls.prototype.countdownStart = function(seconds) { + const countdown = document.getElementById('controls-countdown'); + countdown.dataset.tick = seconds - 1; + + setTimeout(this.countdownTick.bind(this), 1000); +}; + +Controls.prototype.countdownTick = function() { + const countdown = document.getElementById('controls-countdown'); + const tick = countdown.dataset.tick * 1; + countdown.dataset.tick = tick - 1; + + if (tick === 0) { + this.countdownComplete(); + return; + } + + const s = (tick !== 1) ? 's' : ''; + countdown.innerHTML = `${tick} second${s}!`; + setTimeout(this.countdownTick.bind(this), 1000); +}; + +Controls.prototype.countdownComplete = function() { + alert("boom") +}; + Controls.prototype.drawGuesses = function() { const container = document.getElementById('controls-guesses'); container.querySelectorAll('.controls-guess').forEach(el => el.parentNode.removeChild(el)); @@ -58,117 +77,131 @@ Controls.prototype.showWaiting = function() { 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'; + document.getElementById('controls-guesses').style.display = 'none'; + document.getElementById('controls-panic').style.display = 'none'; }; +Controls.prototype.showGuessing = function() { + document.getElementById('controls-start').parentNode.style.display = 'none'; + document.getElementById('controls-walls').parentNode.style.display = 'none'; + document.getElementById('controls-robots').parentNode.style.display = 'none'; + document.getElementById('controls-panic').style.display = 'none'; + + document.getElementById('controls-stop').parentNode.style.display = ''; + // document.getElementById('controls-moves-reset').parentNode.style.display = ''; + document.getElementById('controls-guesses').style.display = ''; +} + +Controls.prototype.showPanic = function() { + this.showGuessing(); + document.getElementById('controls-panic').style.display = ''; +}; //===== Message handlers +Controls.prototype.msgGuess = function(evt) { + const blurbs = [ " has a solution: ", " can do it in ", " says, maybe ", " wagers ", + " reckons ", " is pretty sure it's ", ", confidently: ", " wants it to be ", + " says ", " hazards ", " guesses ", " thinks it might be "]; + const blurb = blurbs[Math.floor(Math.random() * blurbs.length)]; + + const msg = evt.detail; + const guess = msg.guess; + + document.getElementById('controls-panic').querySelector('.controls-alert-urgent').innerHTML = (`${this.names[msg.id]}${blurb}${guess} moves.`); + this.showPanic(); + this.countdownStart(30); +} + Controls.prototype.msgJoin = function() { this.showWaiting(); }; -// Controls.prototype.playersUpdate = function(names) { -// const container = document.getElementById('controls-players'); -// const keys = Object.keys(names); +Controls.prototype.msgMove = function() { + this.moves++; + document.getElementById('controls-moves').innerHTML = this.moves; +}; + +Controls.prototype.msgPlayers = function(evt) { + const container = document.getElementById('controls-players'); + const names = evt.detail.body; + const keys = Object.keys(names); -// if (keys.length > 0) { -// const nobody = document.getElementById('controls-players-nobody'); -// nobody.parentNode.removeChild(nobody); -// } + if (keys.length > 0) { + const nobody = document.getElementById('controls-players-nobody'); + 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; -// } -// } \ No newline at end of file + 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) + }); + + this.names = names; + + container.querySelectorAll('.controls-player').forEach(el => { + const id = el.id.split('player-').pop(); + if (!this.names[id]) { + container.removeChild(el); + } + }); +}; + +Controls.prototype.msgSkip = function() { + this.coundownComplete(); +}; + +Controls.prototype.msgStart = function() { + this.showGuessing(); +}; + +Controls.prototype.msgStop = function() { + this.showWaiting(); +} + +//===== Click handlers + +Controls.prototype.dispatch = function(evt, data) { + const e = (data ? new CustomEvent(evt, { detail: data }) : new Event(evt)); + document.dispatchEvent(e); +} + +Controls.prototype.onClickGuess = function(evt) { + this.dispatch('L-guess', { moves: evt.currentTarget.dataset.value }); +} + +Controls.prototype.onClickRobots = function() { + this.dispatch('L-robots'); +} + +Controls.prototype.onClickSkip = function() { + this.dispatch('L-skip'); +} + +Controls.prototype.onClickStart = function() { + this.dispatch('L-start'); +} + +Controls.prototype.onClickStop = function() { + this.dispatch('L-stop'); +} + +Controls.prototype.onClickWalls = function() { + this.dispatch('L-walls'); +} + +Controls.prototype.onClickReset = function() { + this.moves = 0; + document.getElementById('controls-moves').innerHTML = this.moves; + + this.dispatch('L-reset'); +}; diff --git a/controls.css b/controls.css index d866b42..8278442 100644 --- a/controls.css +++ b/controls.css @@ -28,13 +28,20 @@ margin: 24px 0 12px 0; } -.controls-alert { +.controls-alert-info { background: #21dfae; color: #222; padding: 12px; margin: 24px 0 12px 0; } +.controls-alert-urgent { + background: #DE4F21; + color: #fff; + padding: 12px; + margin: 24px 0 12px 0; +} + .controls-row { align-items: center; display: flex; @@ -48,21 +55,14 @@ width: 100%; } -#controls-players { -} - .controls-player { padding: 8px; } - -#controls-local { -} - .controls-guess { background: none; cursor: pointer; - float: left; + display: inline-block; font-size: 12px; height: 30px; line-height: 30px; @@ -75,12 +75,22 @@ } #controls-footer { - background: #9A748C; bottom: 0; left: 0; padding: 24px; position: absolute; right: 0; + text-align: center; +} + +#controls-footer a { + color: #639699; + font-size: 24px; + text-decoration: none; +} + +#controls-footer a:hover { + text-decoration: underline; } .controls-button { diff --git a/index.css b/index.css index fed6d47..07e9fe9 100644 --- a/index.css +++ b/index.css @@ -12,9 +12,5 @@ PALETTE: 21DFAE Aquamarine 4D3243 Plum DE4F21 Fire - -EXPERIMENTAL: -9A748C -639699 -364B4D +639699 Cadet */ diff --git a/index.html b/index.html index ff141cc..d195482 100644 --- a/index.html +++ b/index.html @@ -75,23 +75,27 @@
Local
Moves:
-
4
-
Reset
+
-
+
Reset
-
-
Timer:
-
0:42
-
Skip
+
+
How many moves to win?
-
-
How many moves to win?
+
+
+ +
+
Countdown:
+
30 seconds!
+
Skip
+
diff --git a/notes.txt b/notes.txt index 50e73b8..c4d481c 100644 --- a/notes.txt +++ b/notes.txt @@ -9,4 +9,5 @@ // TODO tutorial // TODO walls algorigthm // TODO fix overlapping robot arrows -// TODO win declare/add/remove \ No newline at end of file +// TODO win declare/add/remove +// TODO restore state on join \ No newline at end of file diff --git a/server.js b/server.js index 2d4d537..c5e5505 100644 --- a/server.js +++ b/server.js @@ -23,7 +23,7 @@ const Server = { }, messageOthers: (ws, message) => { - DEBUG && console.log(`Sending to other ${wss.clients.size - 1} client(s):`); + DEBUG && console.log(`Sending to other client(s):`); DEBUG && console.log(message); wss.clients.forEach((client) => { @@ -49,7 +49,7 @@ const Server = { DEBUG && console.log(ws.id); G.removePlayer(ws.id); - Server.messageOthers({ type: 'players', body: G.getPlayers() }); + Server.messageOthers(ws, { type: 'players', body: G.getPlayers() }); }, onConnect: (ws, req) => { @@ -75,21 +75,22 @@ const Server = { DEBUG && console.log('Received message: '); DEBUG && console.log(message); - if (!message.head) { + if (!message.type) { DEBUG && console.warn("Unprocessable message: ") DEBUG && console.warn(message); return; } - switch (message.head.type) { + switch (message.type) { case 'guess': - Server.messageAll({ type: 'guess', guess: 29, id: ws.id }); + const santizedGuess = message.rawBody.moves * 1; + santizedGuess && Server.messageAll({ type: 'guess', guess: santizedGuess, id: ws.id }); break; case 'robots': Server.messageAll({ type: 'robots', body: G.getRobots()}); break; - case 'walls': - Server.messageAll({ type: 'walls', body: G.getWalls()}); + case 'skip': + Server.messageAll({ type: 'start' }); break; case 'start': Server.messageAll({ type: 'start' }); @@ -97,6 +98,9 @@ const Server = { case 'stop': Server.messageAll({ type: 'stop' }); break; + case 'walls': + Server.messageAll({ type: 'walls', body: G.getWalls()}); + break; default: console.warn("Unknown message type: ", message.head.type) }