diff --git a/README.txt b/README.txt index 1f6132f..5c555eb 100644 --- a/README.txt +++ b/README.txt @@ -13,18 +13,12 @@ A victory state can be stored by taking a snapshot of the current stack. Icons from [https://game-icons.net](https://game-icons.net) ## TODO -- multi solve doesn't check if one solution is better - replay stack - chat box -- no cancel from name prompt -- limit concurrent players, make sure connections are closed - walls and winstate algorithm +- shuffle colors (easy) +- tutorial (easy) # Final install - move websocket server to /core - dynamic socket server resolution -- restore name prompt - -# Nice to haves -- shuffle colors -- tutorial diff --git a/client/connection.js b/client/connection.js index 308c2da..ed96543 100644 --- a/client/connection.js +++ b/client/connection.js @@ -2,6 +2,7 @@ const Connection = function() { this.retryCounter = 0; + this.name = null; this.socketListeners = { open: this.onOpen.bind(this), @@ -10,8 +11,9 @@ const Connection = function() { }; // Local event listeners - document.addEventListener('L-join', (evt) => { - this.connect(); + document.addEventListener('L-join', (evt) => { + this.name = evt.detail; + this.connect(); }); document.addEventListener('L-newround', () => this.send({ type: 'newround' }) ); @@ -27,14 +29,8 @@ const Connection = function() { document.addEventListener('L-skip', () => this.send({ type: 'skip' }) ); }; -Connection.prototype.connect = function(){ - 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] - // const rawInput = prompt("What is your name?"); - - this.ws = new WebSocket('ws://localhost:8080/ricochet?name=' + rawInput); +Connection.prototype.connect = function() { + this.ws = new WebSocket('ws://localhost:8080/ricochet?name=' + this.name); this.ws.addEventListener('open', this.socketListeners.open); this.ws.addEventListener('error', this.socketListeners.error); @@ -91,6 +87,7 @@ Connection.prototype.onReceiveMessage = function({ data }) { switch (msg.type) { case 'connected': eventName = 'G-connected'; break; case 'countdown': eventName = 'G-countdown'; break; + case 'full': eventName = 'G-full'; break; case 'objective': eventName = 'G-objective'; break; case 'players': eventName = 'G-players'; break; case 'robots': eventName = 'G-robots'; break; diff --git a/client/controls.js b/client/controls.js index 061a44c..ff8857e 100644 --- a/client/controls.js +++ b/client/controls.js @@ -4,15 +4,20 @@ const Controls = function() { this.playerId = null; this.countdownTimer = null; + this.currentMoveCount = 0; + this.winningMoveCount = Infinity; + // "Local" and "Global" messages document.addEventListener('L-conn-error', this.msgConnectionError.bind(this)); document.addEventListener('L-complete', this.msgComplete.bind(this)); document.addEventListener('L-join', this.msgJoin.bind(this)); + document.addEventListener('L-newround', this.msgNewRound.bind(this)); document.addEventListener('L-skip', this.msgSkip.bind(this)); document.addEventListener('L-stack', this.msgStack.bind(this)); document.addEventListener('G-connected', this.msgConnected.bind(this)); document.addEventListener('G-countdown', this.msgCountdown.bind(this)); + document.addEventListener('G-full', this.msgFull.bind(this)); document.addEventListener('G-players', this.msgPlayers.bind(this)); document.addEventListener('G-state', this.msgState.bind(this)); document.addEventListener('G-win', this.msgWin.bind(this)); @@ -97,30 +102,30 @@ Controls.prototype.drawPlayers = function() { }); const keys = Object.keys(this.names); - if (keys.length > 1) { - keys.forEach(connectionId => { - const n = document.createElement('div'); - n.innerHTML = this.playerId === connectionId ? `${this.names[connectionId]} (you)` : this.names[connectionId]; - n.className = 'controls-player'; - container.appendChild(n) - }); - } + + keys.forEach(connectionId => { + const n = document.createElement('div'); + n.innerHTML = this.playerId === connectionId ? `${this.names[connectionId]} (you)` : this.names[connectionId]; + n.className = 'controls-player'; + container.appendChild(n) + }); const msg = container.querySelector('.controls-message'); - msg.innerHTML = (keys.length > 1 ? '' : 'Nobody is in the game yet.'); - msg.style.display = (keys.length > 1 ? 'none' : 'block'); + msg.innerHTML = (keys.length > 0 ? '' : 'Nobody is in the game yet.'); + msg.style.display = (keys.length > 0 ? 'none' : 'block'); }; //===== Message handlers Controls.prototype.msgJoin = function() { this.setState('CONNECTING'); - document.querySelector('#controls-connection .controls-message').innerHTML = 'Connecting...'; }; Controls.prototype.msgComplete = function(evt) { - document.getElementById('controls-moves-solve').style.display = (evt.detail.complete ? 'block' : 'none'); + if (this.currentMoveCount < this.winningMoveCount) { + document.getElementById('controls-moves-solve').style.display = (evt.detail.complete ? 'block' : 'none'); + } }; Controls.prototype.msgConnected = function(evt) { @@ -128,12 +133,24 @@ Controls.prototype.msgConnected = function(evt) { this.drawPlayers(); }; -Controls.prototype.msgConnectionError = function() { +Controls.prototype.msgConnectionError = function(evt) { this.setState('CONNECTING'); - document.querySelector('#controls-connection .controls-message').innerHTML = "Can't reach the server."; + + const msg = (evt.detail === 'noname' + ? "Didn't get your name - refresh to try again." + : "Can't reach the server."); + + document.querySelector('#controls-connection .controls-message').innerHTML = msg; +}; + +Controls.prototype.msgFull = function() { + this.setState('CONNECTING'); + document.querySelector('#controls-connection .controls-message').innerHTML = "Sorry, the game is full right now. Please try again later."; }; Controls.prototype.msgCountdown = function(evt) { + this.winningMoveCount = evt.detail.body.moveCount; + document.getElementById('controls-countdown-moves').innerHTML = evt.detail.body.moveCount; document.getElementById('controls-countdown-player').innerHTML = `${this.names[evt.detail.body.id]} has a solution!`; @@ -152,14 +169,14 @@ Controls.prototype.msgSkip = function() { Controls.prototype.msgStack = function(evt) { const robots = evt.detail.reduce((acc, { id }) => acc.has(id) ? acc : acc.add(id), new Set()); - const moveCount = evt.detail.length - robots.size; + this.currentMoveCount = evt.detail.length - robots.size; document.getElementById('controls-moves-solve').style.display = 'none'; - document.getElementById('controls-moves-count').innerHTML = moveCount; + document.getElementById('controls-moves-count').innerHTML = this.currentMoveCount; - document.getElementById('controls-moves-reset').style.display = moveCount > 0 ? 'block' : 'none'; - document.getElementById('controls-moves-undo').style.display = moveCount > 0 ? 'block' : 'none'; + document.getElementById('controls-moves-reset').style.display = this.currentMoveCount > 0 ? 'block' : 'none'; + document.getElementById('controls-moves-undo').style.display = this.currentMoveCount > 0 ? 'block' : 'none'; }; Controls.prototype.msgPlayers = function(evt) { @@ -176,6 +193,10 @@ Controls.prototype.msgWin = function(evt) { document.getElementById('controls-win-count').innerHTML = evt.detail.body.moveCount; }; +Controls.prototype.msgNewRound = function() { + this.winningMoveCount = Infinite; +}; + //===== Click handlers Controls.prototype.dispatch = function(evt, data) { diff --git a/index.html b/index.html index 31e0351..09da5ef 100644 --- a/index.html +++ b/index.html @@ -86,7 +86,21 @@ new Stack(); // Entry point. - const evt = new Event('L-join'); + + 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] + + // const rawInput = prompt("What is your name?"); + + if (!rawInput) { + const evt = new CustomEvent('L-conn-error', { detail: "noname" }); + document.dispatchEvent(evt); + return; + } + + const evt = new CustomEvent('L-join', { detail: rawInput }); document.dispatchEvent(evt); }) diff --git a/server/ricochet.js b/server/ricochet.js index ee90284..d123b05 100644 --- a/server/ricochet.js +++ b/server/ricochet.js @@ -61,6 +61,11 @@ const Ricochet = function({ messenger }) { //===== Connection management Ricochet.prototype.onConnect = function(ws, req) { + if (Object.keys(this.players).length >= 10) { + this.messenger.messageOne(ws, { type: 'full' }); + return; + } + const url = UrlParser.parse(req.url, true); const query = url.query; const santizedName = (query.name || 'Unknown').replace(/[^\w ]/g, ''); @@ -148,9 +153,7 @@ Ricochet.prototype.freshWalls = function() { // DO NUMBER OF CORNERS FIRST AFTER TESTING for (let n = 0; n < numberOfWalls; n++) { - break; - // const { i: ri, j: rj } = this.randomSquare(); - + const { i: ri, j: rj } = this.randomSquare(); const isHorizontal = Math.random() < 0.5; const isBackward = Math.random() < 0.5; @@ -185,13 +188,13 @@ Ricochet.prototype.freshWalls = function() { Ricochet.prototype.freshObjective = function() { const rand = Math.floor(Math.random() * this.robotIds.length); - const id = this.robotIds[rand]; + const id = this.robotIds[0]; //this.robotIds[rand]; const { i, j } = this.randomUnoccupiedSquare(); return { i, j, - id: this.robotIds[0], //id; + id, color: this.colors[id], }; }; diff --git a/style/controls.css b/style/controls.css index 824e749..aeba7e2 100644 --- a/style/controls.css +++ b/style/controls.css @@ -13,6 +13,7 @@ border: 6px solid #483c6c; border-radius: 0 18px 0 18px; color: #0cffe1; + display: none; font-size: 24px; margin-bottom: 24px; overflow: hidden;