diff --git a/multi/index.js b/multi/index.js index eb529be..07a9531 100644 --- a/multi/index.js +++ b/multi/index.js @@ -2,12 +2,6 @@ process.title = 'mapguesser-multi'; -class State { - static OPEN = 1; - static PLACE_RECEIVED = 2; - static GUESS_SENT = 3; -} - class MultiGame { constructor() { this.rooms = new Map(); @@ -32,7 +26,7 @@ class MultiGame { var member = room.members.get(token); member.connection = connection; - this._sendInitialData(room, member); + this._sendInitialData(room, member, token); } createRoom(roomId) { @@ -58,7 +52,7 @@ class MultiGame { self._sendToMember(member, 'member_joined', data); }); - room.members.set(token, { userName: userName, state: State.OPEN, connection: null }); + room.members.set(token, { userName: userName, connection: null }); } startGame(roomId, places) { @@ -92,14 +86,14 @@ class MultiGame { room.updated = new Date(); var round = room.rounds[room.currentRound]; - var member = this.rooms.get(roomId).members.get(token); - - this._sendResultsUntilNow(room, member); - - round.results.set(member.userName, { guessPosition: guessPosition, distance: distance, score: score }); - member.state = State.GUESS_SENT; + var member = room.members.get(token); + var allResults = this._collectResultsInRound(room, round); this._broadcastGuess(room, member.userName, guessPosition, distance, score); + + round.results.set(token, { guessPosition: guessPosition, distance: distance, score: score }); + + return allResults; } nextRound(roomId, currentRound) { @@ -126,12 +120,10 @@ class MultiGame { var self = this; room.members.forEach(function (member) { self._sendToMember(member, 'new_round', data); - - member.state = State.PLACE_RECEIVED; }); } - _sendInitialData(room, member) { + _sendInitialData(room, member, token) { var data = {}; if (room.currentRound >= 0) { @@ -141,18 +133,23 @@ class MultiGame { data.history = []; for (var i = 0; i < room.currentRound; ++i) { var round = room.rounds[i]; - var result; - if (round.results.has(member.userName)) { - result = round.results.get(member.userName); - } else { - result = { guessPosition: null, distance: null, score: 0 }; - } + + var result = { guessPosition: null, distance: null, score: 0 }; + var allResults = []; + + round.results.forEach(function (currentResult, currentToken) { + if (token === currentToken) { + result = currentResult; + return; + } + + allResults.push({ userName: room.members.get(currentToken).userName, guessPosition: currentResult.guessPosition, distance: currentResult.distance, score: currentResult.score }); + }); data.history.push({ position: round.place.position, - guessPosition: result.guessPosition, - distance: result.distance, - score: result.score + result: result, + allResults: allResults }); } @@ -164,30 +161,30 @@ class MultiGame { this._sendToMember(member, 'initialize', data); } - _sendResultsUntilNow(room, member) { - if (member.state !== State.GUESS_SENT) { - return; - } - - var round = room.rounds[room.currentRound]; - + _collectResultsInRound(room, round) { var results = []; - round.results.forEach(function (result, userName) { - results.push({ userName: userName, guessPosition: result.guessPosition, distance: result.distance, score: result.score }); + round.results.forEach(function (result, token) { + results.push({ + userName: room.members.get(token).userName, + guessPosition: result.guessPosition, + distance: result.distance, + score: result.score + }); }); - this._sendToMember(member, 'results', results); + return results; } _broadcastGuess(room, userName, guessPosition, distance, score) { var data = { userName: userName, guessPosition: guessPosition, distance: distance, score: score }; - - room.members.forEach(function (member) { - if (!member.state !== State.GUESS_SENT) { + var round = room.rounds[room.currentRound]; + var self = this; + room.members.forEach(function (member, token) { + if (!round.results.has(token)) { return; } - this._sendToMember(member, 'guess', data); + self._sendToMember(member, 'guess', data); }); } @@ -224,34 +221,35 @@ var tcpServer = net.createServer(function (socket) { return; } + var response = { data: null }; switch (data.func) { case 'create_room': - multiGame.createRoom(data.args.roomId); + response.data = multiGame.createRoom(data.args.roomId); break; case 'join_room': - multiGame.joinRoom(data.args.roomId, data.args.token, data.args.userName); + response.data = multiGame.joinRoom(data.args.roomId, data.args.token, data.args.userName); break; case 'start_game': - multiGame.startGame(data.args.roomId, data.args.places); + response.data = multiGame.startGame(data.args.roomId, data.args.places); break case 'guess': - multiGame.guess(data.args.roomId, data.args.token, data.args.guessPosition, data.args.distance, data.args.score); + response.data = multiGame.guess(data.args.roomId, data.args.token, data.args.guessPosition, data.args.distance, data.args.score); break; case 'next_round': - multiGame.nextRound(data.args.roomId, data.args.currentRound); + response.data = multiGame.nextRound(data.args.roomId, data.args.currentRound); break; } - socket.write('OK'); + socket.write(JSON.stringify(response)); socket.end(); }); }); diff --git a/public/static/img/markers/marker-blue-empty.svg b/public/static/img/markers/marker-blue-empty.svg new file mode 100644 index 0000000..828000b --- /dev/null +++ b/public/static/img/markers/marker-blue-empty.svg @@ -0,0 +1,10 @@ + + + + diff --git a/public/static/js/game.js b/public/static/js/game.js index 50d705f..d9d2404 100644 --- a/public/static/js/game.js +++ b/public/static/js/game.js @@ -66,12 +66,8 @@ Game.MultiConnector.newRound(json.data); break; - case 'results': - //TODO - break; - case 'guess': - //TODO + Game.MultiConnector.guess(json.data); break; } }; @@ -94,9 +90,17 @@ 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; + Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); + Game.addPositionToResultMap(true); + if (round.result.guessPosition) { + Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); + } + Game.scoreSum += round.result.score; + + for (var j = 0; j < round.allResults.length; ++j) { + var result = round.allResults[j]; + Game.addGuessPositionToResultMap(result.guessPosition, result, true); + } } document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS); @@ -147,7 +151,7 @@ // 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.addPositionToResultMap(); } Game.panoId = data.place.panoId; @@ -156,6 +160,15 @@ document.getElementById('multi').style.visibility = 'hidden'; Game.resetRound(); Game.startNewRound(); + }, + + guess: function (data) { + var resultBounds = Game.map.getBounds(); + + Game.addGuessPositionToResultMap(data.guessPosition, data); + resultBounds.extend(data.guessPosition); + + Game.map.fitBounds(resultBounds); } }, @@ -234,9 +247,10 @@ if (this.response.history) { for (var i = 0; i < this.response.history.length; ++i) { var round = this.response.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; + Game.rounds.push({ position: round.position, guessPosition: round.result.guessPosition, realMarker: null, guessMarkers: [] }); + Game.addPositionToResultMap(true); + Game.addGuessPositionToResultMap(round.result.guessPosition, null, true); + Game.scoreSum += round.result.score; } document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS); @@ -259,9 +273,13 @@ if (round.realMarker) { round.realMarker.setMap(null); } - if (round.guessMarker) { - round.guessMarker.setMap(null); - round.line.setMap(null); + for (var j = 0; j < round.guessMarkers.length; ++j) { + var guessMarker = round.guessMarkers[j]; + guessMarker.marker.setMap(null); + guessMarker.line.setMap(null); + if (guessMarker.info) { + guessMarker.info.close(); + } } } @@ -291,10 +309,15 @@ var lastRound = Game.rounds[Game.rounds.length - 1]; lastRound.realMarker.setVisible(false); - if (lastRound.guessMarker) { - lastRound.guessMarker.setVisible(false); - lastRound.line.setVisible(false); + for (var i = 0; i < lastRound.guessMarkers.length; ++i) { + var guessMarker = lastRound.guessMarkers[i]; + guessMarker.marker.setVisible(false); + guessMarker.line.setVisible(false); + if (guessMarker.info) { + guessMarker.info.close(); + } } + } document.getElementById('panoCover').style.visibility = 'hidden'; @@ -316,7 +339,7 @@ }, startNewRound: function () { - Game.rounds.push({ position: null, guessPosition: null, realMarker: null, guessMarker: null, line: null }); + Game.rounds.push({ position: null, guessPosition: null, realMarker: null, guessMarkers: [] }); document.getElementById('currentRound').innerHTML = String(Game.rounds.length) + '/' + String(Game.NUMBER_OF_ROUNDS); @@ -356,7 +379,7 @@ Game.panorama.setPano(panoId); }, - evaluateGuess: function () { + guess: function () { if (!Game.guessMarker) { return; } @@ -392,13 +415,22 @@ Game.scoreSum += this.response.result.score; document.getElementById('currentScoreSum').innerHTML = String(Game.scoreSum) + '/' + String(Game.rounds.length * Game.MAX_SCORE); - Game.rounds[Game.rounds.length - 1].position = this.response.result.position; - Game.addRealGuessPair(this.response.result.position, guessPosition); - var resultBounds = new google.maps.LatLngBounds(); - resultBounds.extend(this.response.result.position); + + Game.rounds[Game.rounds.length - 1].position = this.response.position; + Game.addPositionToResultMap(); + resultBounds.extend(this.response.position); + Game.addGuessPositionToResultMap(guessPosition); resultBounds.extend(guessPosition); + if (roomId) { + for (var i = 0; i < this.response.allResults.length; ++i) { + var result = this.response.allResults[i]; + Game.addGuessPositionToResultMap(result.guessPosition, result); + resultBounds.extend(result.guessPosition); + } + } + Game.map.setOptions({ draggableCursor: 'grab' }); @@ -431,8 +463,9 @@ }, data); }, - addRealGuessPair: function (position, guessPosition, hidden) { + addPositionToResultMap: function (hidden) { var round = Game.rounds[Game.rounds.length - 1]; + var position = round.position; round.realMarker = new google.maps.Marker({ map: Game.map, @@ -453,20 +486,25 @@ round.realMarker.addListener('click', function () { window.open('https://www.google.com/maps/search/?api=1&query=' + this.getPosition().toUrlValue(), '_blank'); }); + }, - if (!guessPosition) { - return; - } + addGuessPositionToResultMap: function (guessPosition, result, hidden) { + var round = Game.rounds[Game.rounds.length - 1]; + var position = round.position; - round.guessMarker = new google.maps.Marker({ + var guessMarker = { marker: null, line: null, info: null }; + var markerSvg = result ? 'marker-gray-empty.svg' : 'marker-blue-empty.svg'; + var markerLabel = result ? result.userName.charAt(0).toUpperCase() : '?'; + + guessMarker.marker = new google.maps.Marker({ map: Game.map, visible: !hidden, position: guessPosition, zIndex: Game.rounds.length, - clickable: false, + clickable: !!result, draggable: false, icon: { - url: STATIC_ROOT + '/img/markers/marker-gray-empty.svg?rev=' + REVISION, + url: STATIC_ROOT + '/img/markers/' + markerSvg + '?rev=' + REVISION, size: new google.maps.Size(24, 32), scaledSize: new google.maps.Size(24, 32), anchor: new google.maps.Point(12, 32), @@ -477,11 +515,11 @@ fontFamily: 'Roboto', fontSize: '16px', fontWeight: '500', - text: '?' + text: markerLabel } }); - round.line = new google.maps.Polyline({ + guessMarker.line = new google.maps.Polyline({ map: Game.map, visible: !hidden, path: [ @@ -504,6 +542,19 @@ draggable: false, editable: false }); + + if (result) { + guessMarker.info = new google.maps.InfoWindow({ + content: '

' + result.userName + '

' + + '

' + Util.printDistanceForHuman(result.distance) + ' | ' + result.score + ' points

', + }); + + guessMarker.marker.addListener('click', function () { + guessMarker.info.open(Game.map, this); + }); + } + + round.guessMarkers.push(guessMarker); }, calculateScoreBarProperties: function (score, maxScore) { @@ -555,14 +606,14 @@ }); round.realMarker.setVisible(true); - if (round.guessMarker) { - round.guessMarker.setVisible(true); - round.line.setVisible(true); - } - resultBounds.extend(round.position); - if (round.guessMarker) { - resultBounds.extend(round.guessPosition); + + for (var j = 0; j < round.guessMarkers.length; ++j) { + var guessMarker = round.guessMarkers[j]; + guessMarker.marker.setVisible(true); + guessMarker.line.setVisible(true); + + resultBounds.extend(guessMarker.marker.getPosition()); } } @@ -639,7 +690,7 @@ clickable: false, draggable: true, icon: { - url: STATIC_ROOT + '/img/markers/marker-gray-empty.svg?rev=' + REVISION, + url: STATIC_ROOT + '/img/markers/marker-blue-empty.svg?rev=' + REVISION, size: new google.maps.Size(24, 32), scaledSize: new google.maps.Size(24, 32), anchor: new google.maps.Point(12, 32), @@ -687,7 +738,7 @@ } document.getElementById('guessButton').onclick = function () { - Game.evaluateGuess(); + Game.guess(); } document.getElementById('continueButton').onclick = function () { diff --git a/src/Controller/GameFlowController.php b/src/Controller/GameFlowController.php index 8a02fbe..e668489 100644 --- a/src/Controller/GameFlowController.php +++ b/src/Controller/GameFlowController.php @@ -61,9 +61,11 @@ class GameFlowController $round = $state['rounds'][$i]; $response['history'][] = [ 'position' => $round['position']->toArray(), - 'guessPosition' => $round['guessPosition']->toArray(), - 'distance' => $round['distance'], - 'score' => $round['score'] + 'result' => [ + 'guessPosition' => $round['guessPosition']->toArray(), + 'distance' => $round['distance'], + 'score' => $round['score'] + ] ]; } @@ -126,11 +128,8 @@ class GameFlowController $last['score'] = $result['score']; $response = [ - 'result' => [ - 'position' => $last['position']->toArray(), - 'distance' => $result['distance'], - 'score' => $result['score'] - ] + 'position' => $last['position']->toArray(), + 'result' => $result ]; $state['rounds'][$state['currentRound']] = $last; @@ -165,20 +164,18 @@ class GameFlowController $guessPosition = new Position((float) $this->request->post('lat'), (float) $this->request->post('lng')); $result = $this->evalueteGuess($last['position'], $guessPosition, $state['area']); - $this->multiConnector->sendMessage('guess', [ + $allResults = $this->multiConnector->sendMessage('guess', [ 'roomId' => $roomId, 'token' => $multiState['token'], - 'guess' => $guessPosition->toArray(), + 'guessPosition' => $guessPosition->toArray(), 'distance' => $result['distance'], 'score' => $result['score'] ]); $response = [ - 'result' => [ - 'position' => $last['position']->toArray(), - 'distance' => $result['distance'], - 'score' => $result['score'] - ] + 'position' => $last['position']->toArray(), + 'result' => $result, + 'allResults' => $allResults ]; return new JsonContent($response); diff --git a/src/Multi/MultiConnector.php b/src/Multi/MultiConnector.php index d74535e..c874966 100644 --- a/src/Multi/MultiConnector.php +++ b/src/Multi/MultiConnector.php @@ -2,7 +2,7 @@ class MultiConnector { - public function sendMessage(string $func, array $args = []): void + public function sendMessage(string $func, array $args = []) { $message = json_encode([ 'func' => $func, @@ -17,8 +17,10 @@ class MultiConnector } fclose($connection); - if ($response !== 'OK') { - throw new \Exception('Sending message failed with response: ' . $response); + $response = json_decode($response, true); + + if (isset($response['data'])) { + return $response['data']; } } }