diff --git a/public/static/css/game.css b/public/static/css/game.css index 9887cc7..439676a 100644 --- a/public/static/css/game.css +++ b/public/static/css/game.css @@ -102,6 +102,15 @@ display: none; } +#startMultiGameButton { + display: none; +} + +#players > p { + font-size: 14px; + font-weight: bold; +} + @media screen and (max-width: 599px) { #mapName { display: none; diff --git a/public/static/js/game.js b/public/static/js/game.js index 0cb0616..7f1016f 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -5,6 +5,8 @@ NUMBER_OF_ROUNDS: 5, MAX_SCORE: 1000, + mapBounds: null, + multi: { token: null, owner: false }, rounds: [], scoreSum: 0, panoId: null, @@ -15,8 +17,190 @@ adaptGuess: false, googleLink: null, - initialize: function () { + MultiConnector: { + connection: null, + reconnectCounter: 0, + + connect: function () { + if (Game.MultiConnector.connection && Game.MultiConnector.connection.readyState !== WebSocket.CLOSED) { + return; + } + + Game.MultiConnector.connection = new WebSocket((MapGuesser.isSecure ? 'wss' : 'ws') + '://' + multiUrl); + + Game.MultiConnector.connection.onopen = function () { + document.getElementById('loading').style.visibility = 'hidden'; + + Game.MultiConnector.reconnectCounter = 0; + + Game.MultiConnector.connection.send(JSON.stringify({ func: 'connect_to_room', args: { roomId: roomId, token: Game.multi.token } })); + }; + + Game.MultiConnector.connection.onclose = Game.MultiConnector.noConnection; + + Game.MultiConnector.connection.onerror = function (event) { + console.error('WebSocket error in Game.MultiConnector:', event); + }; + + Game.MultiConnector.connection.onmessage = function (message) { + var json; + + try { + json = JSON.parse(message.data); + } catch (e) { + console.error('Cannot parse message!'); + console.error(message.data); + return; + } + + switch (json.type) { + case 'initialize': + Game.MultiConnector.initialize(json.data); + break; + + case 'member_joined': + Game.MultiConnector.memberJoined(json.data); + break; + + case 'new_round': + Game.MultiConnector.newRound(json.data); + break; + + case 'results': + //TODO + break; + + case 'guess': + //TODO + break; + } + }; + }, + + noConnection: function () { + if (Game.MultiConnector.reconnectCounter === 2) { + console.error('Could not reconnect WebSocket for Game.MultiConnector...') + } + + setTimeout(function () { + Game.MultiConnector.reconnectCounter++; + + console.log('Reconnecting WebSocket for Game.MultiConnector... ' + Game.MultiConnector.reconnectCounter); + Game.MultiConnector.connect(); + }, 1000 + Math.min(Game.MultiConnector.reconnectCounter * 500, 9000)); + }, + + initialize: function (data) { + if (data.history) { + for (var i = 0; i < data.history.length; ++i) { + var round = data.history[i]; + Game.rounds.push({ position: round.position, guessPosition: round.guessPosition, realMarker: null, guessMarker: null, line: null }); + Game.addRealGuessPair(round.position, round.guessPosition, true); + Game.scoreSum += round.score; + } + + document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS); + document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE); + } + + if (data.place) { + Game.panoId = data.place.panoId; + Game.pov = data.place.pov; + + document.getElementById('panoCover').style.visibility = 'hidden'; + MapGuesser.hideModal(); + + Game.startNewRound(); + } + + document.getElementById('loading').style.visibility = 'hidden'; + + var div = document.getElementById('players'); + + for (var i = 0; i < data.members.length; ++i) { + var member = data.members[i]; + + var p = document.createElement('p'); + p.innerHTML = member.userName + (member.me ? ' (me)' : ''); + div.appendChild(p); + } + }, + + memberJoined: function (data) { + var div = document.getElementById('players'); + + var p = document.createElement('p'); + p.innerHTML = data.userName; + div.appendChild(p); + }, + + newRound: function (data) { + //TODO: workaround until results are not sent + if (Game.adaptGuess) { + document.getElementById('guess').classList.remove('adapt'); + } + + if (Game.rounds.length === Game.NUMBER_OF_ROUNDS) { + Game.reset(); + } + + // if player didn't guess - TODO: show everything on a map + if (data.result && Game.rounds.length > 0 && !Game.rounds[Game.rounds.length - 1].position) { + Game.rounds[Game.rounds.length - 1].position = data.result.position; + Game.addRealGuessPair(data.result.position, null); + } + + Game.panoId = data.place.panoId; + Game.pov = data.place.pov; + + MapGuesser.hideModal(); + Game.resetRound(); + Game.startNewRound(); + } + }, + + prepare: function () { + var data = new FormData(); + var userNames; + + if (roomId) { + var userNames = localStorage.userNames ? JSON.parse(localStorage.userNames) : {}; + if (!userNames.hasOwnProperty(roomId)) { + userNames[roomId] = prompt('Your name: '); + localStorage.userNames = JSON.stringify(userNames); + } + + data.append('userName', userNames[roomId]); + } + document.getElementById('loading').style.visibility = 'visible'; + + var url = roomId ? '/multiGame/' + roomId + '/prepare.json' : '/game/' + mapId + '/prepare.json'; + MapGuesser.httpRequest('POST', url, function () { + document.getElementById('loading').style.visibility = 'hidden'; + + document.getElementById('mapName').innerHTML = this.response.mapName; + Game.mapBounds = this.response.bounds; + + Game.initialize(); + + if (roomId) { + Game.multi.token = this.response.token; + Game.multi.owner = this.response.owner; + + MapGuesser.showModal('multi'); + if (Game.multi.owner) { + document.getElementById('startMultiGameButton').style.display = 'block'; + } + + document.getElementById('loading').style.visibility = 'visible'; + + Game.MultiConnector.connect(); + } + }, data); + }, + + initialize: function () { document.getElementById('panoCover').style.visibility = 'visible'; document.getElementById('currentRound').innerHTML = '1/' + String(Game.NUMBER_OF_ROUNDS); document.getElementById('currentScoreSum').innerHTML = '0/0'; @@ -24,9 +208,16 @@ Game.map.setOptions({ draggableCursor: 'crosshair' }); - Game.map.fitBounds(mapBounds); + Game.map.fitBounds(Game.mapBounds); - MapGuesser.httpRequest('GET', '/game/' + mapId + '/initialData.json', function () { + if (roomId) { + // if it is multiplayer mode, data is sent via WS + return; + } + + document.getElementById('loading').style.visibility = 'visible'; + + MapGuesser.httpRequest('POST', '/game/' + mapId + '/initialData.json', function () { document.getElementById('loading').style.visibility = 'hidden'; document.getElementById('panoCover').style.visibility = 'hidden'; @@ -63,8 +254,10 @@ for (var i = 0; i < Game.rounds.length; ++i) { var round = Game.rounds[i]; - if (round.realMarker && round.guessMarker && round.line) { + if (round.realMarker) { round.realMarker.setMap(null); + } + if (round.guessMarker) { round.guessMarker.setMap(null); round.line.setMap(null); } @@ -96,8 +289,10 @@ var lastRound = Game.rounds[Game.rounds.length - 1]; lastRound.realMarker.setVisible(false); - lastRound.guessMarker.setVisible(false); - lastRound.line.setVisible(false); + if (lastRound.guessMarker) { + lastRound.guessMarker.setVisible(false); + lastRound.line.setVisible(false); + } } document.getElementById('panoCover').style.visibility = 'hidden'; @@ -108,7 +303,12 @@ Game.map.setOptions({ draggableCursor: 'crosshair' }); - Game.map.fitBounds(mapBounds); + Game.map.fitBounds(Game.mapBounds); + + if (roomId) { + // if it is multiplayer mode, data is sent via WS + return; + } Game.startNewRound(); }, @@ -124,8 +324,14 @@ handleErrorResponse: function (error) { // for the time being we only handle the "no_session_found" error and reset the game - MapGuesser.httpRequest('GET', '/game/' + mapId + '/json', function () { - mapBounds = this.response.bounds; + if (roomId) { + //TODO: better error message + alert('Your session is invalid, please start multiplayer again!') + return; + } + + MapGuesser.httpRequest('GET', '/game/' + mapId + '/prepare.json', function () { + Game.mapBounds = this.response.bounds; Game.reset(); }); @@ -160,7 +366,8 @@ data.append('lat', String(guessPosition.lat)); data.append('lng', String(guessPosition.lng)); - MapGuesser.httpRequest('POST', '/game/' + mapId + '/guess.json', function () { + var url = roomId ? '/multiGame/' + roomId + '/guess.json' : '/game/' + mapId + '/guess.json'; + MapGuesser.httpRequest('POST', url, function () { if (this.response.error) { Game.handleErrorResponse(this.response.error); return; @@ -204,6 +411,10 @@ Game.panoId = this.response.place.panoId; Game.pov = this.response.place.pov; } else { + if (!Game.multi.owner) { + //TODO: "waiting for" disabled button + document.getElementById('continueButton').style.display = 'none'; + } Game.panoId = null; Game.pov = null; } @@ -233,6 +444,10 @@ window.open('https://www.google.com/maps/search/?api=1&query=' + this.getPosition().toUrlValue(), '_blank'); }); + if (!guessPosition) { + return; + } + round.guessMarker = new google.maps.Marker({ map: Game.map, visible: !hidden, @@ -304,7 +519,10 @@ scoreInfo.children[0].style.display = 'none'; scoreInfo.children[1].style.display = 'block'; document.getElementById('showSummaryButton').style.display = null; - document.getElementById('startNewGameButton').style.display = 'block'; + + if (Game.multi.owner) { + document.getElementById('startNewGameButton').style.display = 'block'; + } var resultBounds = new google.maps.LatLngBounds(); @@ -325,12 +543,17 @@ fontWeight: '500', text: String(i + 1) }); + round.realMarker.setVisible(true); - round.guessMarker.setVisible(true); - round.line.setVisible(true); + if (round.guessMarker) { + round.guessMarker.setVisible(true); + round.line.setVisible(true); + } resultBounds.extend(round.position); - resultBounds.extend(round.guessPosition); + if (round.guessMarker) { + resultBounds.extend(round.guessPosition); + } } Game.map.fitBounds(resultBounds); @@ -378,13 +601,7 @@ } }; - MapGuesser.sessionAvailableHooks.reinitializeGame = function () { - MapGuesser.httpRequest('GET', '/game/' + mapId + '/json', function () { - mapBounds = this.response.bounds; - - Game.initialize(); - }); - }; + MapGuesser.sessionAvailableHooks.reinitializeGame = Game.prepare; if (!('ontouchstart' in document.documentElement)) { Game.adaptGuess = true; @@ -445,7 +662,9 @@ Game.rewriteGoogleLink(); }); - Game.initialize(); + if (COOKIES_CONSENT) { + Game.prepare(); + } document.getElementById('showGuessButton').onclick = function () { this.style.visibility = 'hidden'; @@ -462,7 +681,19 @@ } document.getElementById('continueButton').onclick = function () { - Game.resetRound(); + if (roomId) { + if (!Game.multi.owner) { + return; + } + + document.getElementById('loading').style.visibility = 'visible'; + + MapGuesser.httpRequest('POST', '/multiGame/' + roomId + '/nextRound.json', function () { + document.getElementById('loading').style.visibility = 'hidden'; + }); + } else { + Game.resetRound(); + } } document.getElementById('showSummaryButton').onclick = function () { @@ -470,6 +701,31 @@ } document.getElementById('startNewGameButton').onclick = function () { - Game.reset(); + if (roomId) { + if (!Game.multi.owner) { + return; + } + + document.getElementById('loading').style.visibility = 'visible'; + + MapGuesser.httpRequest('POST', '/multiGame/' + roomId + '/initialData.json', function () { + document.getElementById('loading').style.visibility = 'hidden'; + }); + } else { + Game.reset(); + } + } + + document.getElementById('startMultiGameButton').onclick = function () { + if (!roomId || !Game.multi.owner) { + return; + } + + MapGuesser.hideModal(); + document.getElementById('loading').style.visibility = 'visible'; + + MapGuesser.httpRequest('POST', '/multiGame/' + roomId + '/initialData.json', function () { + document.getElementById('loading').style.visibility = 'hidden'; + }); } })(); diff --git a/public/static/js/mapguesser.js b/public/static/js/mapguesser.js index 8af9ffe..89a79a9 100644 --- a/public/static/js/mapguesser.js +++ b/public/static/js/mapguesser.js @@ -1,4 +1,5 @@ var MapGuesser = { + isSecure: window.location.protocol === 'https:', cookiesAgreed: false, sessionAvailableHooks: {}, diff --git a/views/game.php b/views/game.php index c772f29..10acef6 100644 --- a/views/game.php +++ b/views/game.php @@ -5,10 +5,19 @@ @extends(templates/layout_full) +@section(pagemodal) +
Waiting for players...
+ + +